1.6.0 프로젝트 개요
JPG, GIF등의 이미지 파일을 NSImage를 이용하여 화면에 출력하고 , 확대 축소를 해주는 간단한 이미지 뷰어 프로그램을 만들어 보겠습니다.
사용자 삽입 이미지

1.6.1 프로젝트 생성
이전 포스트에서 경험 해 본 코코아 프로젝트와 소스파일을 생성하고, 인터페이스 빌더와 연결 할 수 있다는 전제 하에 설명하겠습니다. 이 부분에 이해가 안되시면 1.2와 1.3 포스트를 해보시고 오시기 바랍니다.

Xcode를 실행하고 아래와 같이 SimpleDraw 코코아 프로젝트를 생성합니다.
  1. 메뉴바에서 File/New Project...를 선택합니다.
  2. Application/Cocoa Application을 선택합니다.
  3. project Name에 SimpleViewer를 입력하고 finish 버튼을 클릭합니다.

1.6.2 인터페이스 빌더에서 작업

1) 서브 클래스 생성

Nib 파일을 클릭하여 인터페이스빌더를 열고 NSView 클래스의 서브클래스를 만들고(아래의 창에서 NSView에 마우스 우클릭 하여 Subclass NSView 클릭) 이름을 ImgView로 변경합니다.
사용자 삽입 이미지

위와 같은 방법으로 AppController란 NSObject의 서브클래스를 만듭니다. 서브클래스를 만든 후에 AppController를 우클릭하여 나오는 메뉴에서 Instantiate AppController를 클릭하여 인스턴스를 생성합니다.
사용자 삽입 이미지


2) 컨트롤 생성 및 속성 설정

윈도우를 열고 팔레트의 Containers 항목에서  CustomeView와 Controls 항목에서 Slider를 드래그해서 아래와 같이 윈도우에 배치 합니다.

CutomeView를 클릭 후에 인스펙터(command+5)를 불러 내어 Custom Class 항목에서, 이전에 만들어 놓은 ImgView를 선택하고 아래와 같이 CustomeView가 ImgView로 변경됨을 확인합니다.
사용자 삽입 이미지

Size 항목에서 하단의 Autosizing 항목에서 아래와 같이 사각형내의 선들을  마우스로 클릭하여 스프링처럼 나오게 만듭니다. 이 작업은 윈도우의 크기가 변경될 때 상하좌우 크기가 윈도우에 맞게 자동으로 변경되게 합니다.
사용자 삽입 이미지

슬라이더의 속성을 아래와 같이 설정 합니다. Minimum은 최소값, Maximum은 최대값, Current는 현재값, Number of Markers는 슬라이더 상단의 눈금의 갯수를 의미합니다.
사용자 삽입 이미지

슬라이더도 윈도우의 크기에 맞추어 변경되게 하기위해, 아래와 같이 Size 항목에서 하단의 Autosizing의 사각형 내부의 좌우선을 클릭하여 스프링이 되게 만듭니다. 이 작업은 슬라이더의 높이는 유지하며 너비가 윈도우의 크기와 함께 변하도록 만들어 줍니다.
사용자 삽입 이미지

3) 연결

사용자 삽입 이미지
인터페이스 빌더의 Instances항목에서 AppController의 인스펙터를 불러내어 imgView 아울렛을 추가하고, Type을 ImgView로 선택합니다.

인스펙터는 항목을 마우스로 클릭하여 포커스를 준 후에 shift+command+i 또는 commad+숫자 를 입력하여 불러낼 수 있습니다.

이 작업은 AppController에서 ImgView를 제어할 수 있도록 합니다.










사용자 삽입 이미지
이제 Actions 항목을 선택하고 resizeImage와 openImage 액션을 추가합니다.

resizeImage는 하단의 슬라이더가 변경될 때, 이미지 크기를 변경하고, openImage는 메뉴에서 파일을 오픈할 때 처리하기 위한 액션입니다.




control 키를 누른 상태에서 AppController를 드래그하여  ImgView에 놓습니다. 아래와 같이 AppControll의 ImgView 아울렛에 연결(connect 버튼을 클릭 후, 원모양 아이콘을 확인)합니다.
사용자 삽입 이미지

control 키를 누른 상태에서 슬라이더를 드래그하여 AppController에 놓습니다. Taget/Action에서 resizeImage에 연결합니다.
사용자 삽입 이미지

아래와 같이 메인메뉴의 File에서 오픈을 control 키를 누른 상태에서 드래그하여 AppController에 놓습니다. Target/Action에서 openImage에 연결합니다. 이 작업으로 사용자가 메뉴에서 Open을 클릭하면 AppController의 openImage를 호출합니다.
사용자 삽입 이미지

이제 인스펙트빌더에서 1차적인 작업이 완료되었습니다. Classes에서 AppController와 ImgView를 각각 우클릭하여 CreateFiles  for AppController(and Img View)를 선택하여 소스파일을 생성하고 Xcode로 돌아 갑니다.


1.6.3 소스코드 수정
1) AppController.h 수정

인터페이스 빌더에서의 작업으로 헤더파일에서 해야 할 작업은 거의 없습니다. 다만 컴파일 시 오류를 막기 위해 아래와 같이 @class ImgView; 란 라인을 추가합니다.
#import <Cocoa/Cocoa.h>

@class ImgView;

@interface AppController : NSObject
{
    IBOutlet ImgView *imgView;
}
- (IBAction)openImage:(id)sender;
- (IBAction)resizeImage:(id)sender;
@end


2) AppController.m 수정

#import "AppController.h"
#import "ImgView.h"

@implementation AppController

- (void)openPanelDidEnd:(NSOpenPanel *)openPanel
             returnCode:(int)returnCode
            contextInfo:(void *)x
{   
    NSString *path;
    NSImage *image;
   
    if (returnCode == NSOKButton) {
        path = [openPanel filename];
   
        NSImageRep *imgRep = [NSImageRep imageRepWithContentsOfFile:path];
        NSSize frameSize = [imgRep size];
       
        image = [[NSImage alloc] initWithContentsOfFile:path];
       
        [imgView setImage:image];
        [imgView setFrameSize:frameSize];
        [image release];
    }
}

- (IBAction)openImage:(id)sender
{
    NSOpenPanel *panel = [NSOpenPanel openPanel];
   
    [panel beginSheetForDirectory:nil
                             file:nil
                            types:[NSImage imageFileTypes]
                   modalForWindow:[imgView window]
                    modalDelegate:self
                   didEndSelector:
        @selector(openPanelDidEnd:returnCode:contextInfo:)
                      contextInfo:NULL];
}

- (IBAction)resizeImage:(id)sender
{
    [imgView setRatio:[sender floatValue]];
}

@end

#import "ImgView.h"
ImgView 클래스를 사용하기 때문에 위와 같이 ImgView.h를 인클루드 시킵니다.

아래의 메소드는 파일 오픈을 클릭하였을 경우, 파일선택 창에서 파일 선택이 완료 된 후에 불리어 지는 콜백 함수입니다. 이 메소는 파일오픈 판넬을 오픈할 때 등록하여 주며, 나중에 다시 설명하겠습니다.
- (void)openPanelDidEnd:(NSOpenPanel *)openPanel
             returnCode:(int)returnCode
            contextInfo:(void *)x
{   
    NSString *path;
    NSImage *image;

사용자가 파일 창에서 [열기] 버튼을 클릭하였을 경우에만 실행되도록 합니다.  
    if (returnCode == NSOKButton) {
        path = [openPanel filename];
   
파일 원본 크기를 알기 위해 NSImageRep 오브젝트를 사용합니다.
        NSImageRep *imgRep = [NSImageRep imageRepWithContentsOfFile:path];
        NSSize frameSize = [imgRep size];
     
NSImage에 이미지 파일을 등록합니다.  
        image = [[NSImage alloc] initWithContentsOfFile:path];
       
imgView에 이미지를 설정하고, imgView의 크기를 이미지의 원본크기로 변경합니다.
        [imgView setImage:image];
        [imgView setFrameSize:frameSize];
        [image release];
    }
}

다음은 사용자가 파일오픈을 선택하였을 때 호출되는 - (IBAction)openImage:(id)sender 메소드에서 파일선택 판넬을 오픈하기 위해 아래와 같이 추가합니다.
    NSOpenPanel *panel = [NSOpenPanel openPanel];
   
    [panel beginSheetForDirectory:nil
                             file:nil
                            types:[NSImage imageFileTypes]
                   modalForWindow:[imgView window]
                    modalDelegate:self
                   didEndSelector:
        @selector(openPanelDidEnd:returnCode:contextInfo:)
                      contextInfo:NULL];
모달(판넬이 떠있을 때는 닫기전에는 소유 윈도우는 제어되지 않습니다)로 뛰우며 선택 가능한 파일 종류, 소유 윈도우, 콜백함수를 설정합니다.

@selector(openPanelDidEnd:returnCode:contextInfo:) 이 부분에서 파일선택이 완료되면 위에서 작성한 openPanelDidEnd 메소드가 호출되도록 하여 줍니다.

[imgView setRatio:[sender floatValue]];
슬라이더가 변경되면 호출되는 resizeImage 메소드에서 imgView가 크기를 변경하도록 setRatio를 호출합니다. 이 메소드는 다음 ImgView 클랙스에서 작성되어질 것 입니다.

3) ImgView.h 수정

#import <Cocoa/Cocoa.h>

@interface ImgView : NSView
{
    NSImage *image;
    NSSize orgSize;
    float ratio;
}

- (void)setImage:(NSImage *)img;
- (void)setRatio:(float)r;

@end

NSImage *image;

이미지 출력을 위해 NSImage 클래스를 선언 합니다.

NSSize orgSize;

이미지의 원본 크기를 저장합니다.

float ratio;
이미지 확대/축소 비율을 저장합니다. 0이 최소, 20이 최대, 10이 원본크기를 출력합니다.

- (void)setImage:(NSImage *)img;
선택된 이미지를 image에 설정하는 메소드입니다. 파일메뉴에서 파일 선택 시 AppController에서 호출합니다.

- (void)setRatio:(float)r;
확대/축소 비율을 설정합니다. 슬라이더 변경 시 AppController에서 호출합니다.

4) ImgView.m 수정

#import "ImgView.h"

@implementation ImgView

- (id)initWithFrame:(NSRect)frameRect
{
    if ((self = [super initWithFrame:frameRect]) != nil) {
        // Add initialization code here
        ratio = 10;
    }
    return self;
}

- (void)drawRect:(NSRect)rect
{
    [[NSColor whiteColor] set];
    [NSBezierPath fillRect:rect];
   
    if(image) {
        NSRect imgRect;
        NSRect drawRect;
       
        if(ratio != 0.0f) {
            imgRect.origin = NSZeroPoint;
            imgRect.size = orgSize;
       
            drawRect = [self bounds];
       
            NSLog(@"drawRect: %f, %f", drawRect.size.width, drawRect.size.height);
           
            [image drawInRect:drawRect
                     fromRect:imgRect
                    operation:NSCompositeSourceOver
                     fraction:1.0];
        }   
    }
}

- (void)setImage:(NSImage *)img
{
    [img retain];
    [image release];
   
    image = img;
   
    orgSize = [image size];
   
    [self setNeedsDisplay:YES];
}

- (void)setRatio:(float)r
{
    NSSize viewSize;
    ratio = r;
   
    NSLog(@"RATIO: %f", ratio);
   
    viewSize.width = (orgSize.width * ratio)/10;
    viewSize.height = (orgSize.height * ratio)/10;
   
    [self setFrameSize:viewSize];
    [self setNeedsDisplay:YES];
}

- (void) dealloc
{
    [image release];
    [super dealloc];
}

@end

ratio = 10;
initWithFrame는 ImgView가 초기화될 때, 자동으로 호출되는 메소드 입니다. 이곳에서 초기 오브젝트를 생성하거나 변수를 초기화 합니다. 이미지 선택시 원본 크기로 보이게 하기 위해 ratio를 10으로 설정합니다.

다음은 drawRect 메소드 내에 아래의 라인을 추가합니다. drawRect는 ImgView가 그려져야 할 필요가 있을 때 자동으로 불려지는 메소드 입니다. 이곳에서 그려질 내용들을 처리 합니다.

[[NSColor whiteColor] set];
[NSBezierPath fillRect:rect];
배경색을 흰색으로 채웁니다.

이미지 오브젝트가 설정되었을 때만 출력합니다.
    if(image) {
        NSRect imgRect;
        NSRect drawRect;

확대비율이 0일때는 출력하지 않습니다.  
        if(ratio != 0.0f) {
            imgRect.origin = NSZeroPoint;
            imgRect.size = orgSize;
       
            drawRect = [self bounds];

이미지를 출력합니다.  
            [image drawInRect:drawRect
                     fromRect:imgRect
                    operation:NSCompositeSourceOver
                     fraction:1.0];
        }   
    }
}

아래의 setImgae에서는 사용자가 선택한 파일을 image에 설정합니다.
- (void)setImage:(NSImage *)img
{
    [img retain];
    [image release];
 
image를 설정하고 원본 사이즈의 크기를 저장합니다.
    image = img;
    orgSize = [image size];
다시 그려지도록 알려주며, 이 위의 drawRect가 호출됩니다.  
    [self setNeedsDisplay:YES];
}

아래의 setRatio에서는 사용자가 슬라이더의 변경시에, 크기를 변경해 주는 작업을 합니다.
- (void)setRatio:(float)r
{
    NSSize viewSize;
    ratio = r;

10을 원본 크기로 비율에 맞게 크기를 조절합니다.  
    viewSize.width = (orgSize.width * ratio)/10;
    viewSize.height = (orgSize.height * ratio)/10;

ImgView의 크기를 현재 배율에 맞게 변경합니다.  
    [self setFrameSize:viewSize];
다시 그려지도록 알려주며, 이 위의 drawRect가 호출됩니다.
    [self setNeedsDisplay:YES];
}

메모리에서 해제합니다.
- (void) dealloc
{
    [image release];
    [super dealloc];
}

이제 모든 변경사항을 저장하고 프로젝트를 빌드 후에, 이미지 파일을 선택하여 테스트 해봅니다. 이미지를 불러보고 윈도우의 크기를 변경해 봅니다. 아직 스크롤을 처리하지 않아 커다란 이미지는 제대로 보실 수 없을 것입니다. 이는 다음장에서 처리해 보겠습니다.

AND