보통 Table View의 Cell은 코드로 만드는 경우가 많습니다. 여기서는 인터페이스빌더에서 Table View Cell을 사용하는 간단한 샘플을 만들어 보겠습니다.





1. 프로젝트 생성과 AppDelegate 변경
Xcode를 실행하고 "New Project"를 클릭하여 "Window-based Application"을 선택하여 프로젝트를 생성합니다. 저는 그냥 IBCell이란 이름으로 프로젝트를 만들었습니다.

1) IBCellAppDelegate.h

@class TableViewController;


@interface IBCellAppDelegate : NSObject <UIApplicationDelegate> {


IBOutlet TableViewController *tableViewController;

    UIWindow *window;

}

2
인터페이스 빌더에서 연결을 위해 tableViewController의 아울렛 변수를 추가합니다.

2) IBCellAppDelegate.m

- (void)applicationDidFinishLaunching:(UIApplication *)application {

  

[window addSubview:[tableViewController view]];

    // Override point for customization after application launch

    [window makeKeyAndVisible];

}


메인 윈도우의 SubView로 tableViewController의 View를 추가합니다.

2. ViewController
1) 생성
UIViewController subclass로 TableViewController란 이름으로 클래스를 생성합니다. SDK 3 부터는 "With XIB for user interface"가 있어 선택할 경우 ViewController 오브젝트와 연결된 별도의 xib를 자동으로 생성하여 줍니다. 여기서는 편의를 위해 선택하지 않았습니다.
* 2009/06/26 추가
오늘 확인해 보니 Objective-C class 선택시에 하단에 Subclass of로 선택할 수 있는 클래스들이 나오네요. 버젼업되면서 왜 줄었나 했더니 여기로 들어 가 있었습니다. 위의 방법말고 아래에서 UITableViewController를 바로 선택하고 사용하시면 됩니다.
 

2) TableViewController.h

#import <UIKit/UIKit.h>



@interface TableViewController : UITableViewController {

IBOutlet UITableViewCell *myCell;

}


- (IBAction)goMyBlog:(id)sender;


@end


부모 클래스를 UIViewController에서 UITableViewController로 변경합니다. 그리고 인터페이스빌더에서 연결을 위해 myCell 아울렛 변수를 추가합니다. 그리고 버튼 클릭시 연결될 goMyBlog 액션 메소드를 선언합니다.

3) TableViewController.m

/* 버튼 클릭시 */

- (IBAction)goMyBlog:(id)sender {

NSURL *url = [NSURL URLWithString:@"http://www.cocoadev.co.kr/"];

[[UIApplication sharedApplication] openURL:url];

}


#pragma mark Table view methods


/* 색션 */

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

return 1;

}


/* 색션 타이틀 */

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {

return @"소개";

}


/* */

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

return myCell;

}


/* 높이 */

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

return [myCell frame].size.height;

}


버튼 클릭시 실행될 메소드와 tableView의 delegate, datasource 메소드를 구현합니다.



3. 인터페이스 빌더

1) Table View Controller

인터페이스 빌더를 실행하고 라이브러리의 Controlls의 Table View Controller를 MainWindow.xib로 드래그해서 가지고 옵니다. Class를 TableViewController로 지정합니다.



좌측과 같이 Table View의 Style 속성을 Grouped로 지정합니다.



2) Table View Cell

라이브러리에서 Table View Cell을 MainWindow.xib로 드래그해서 가지고 옵니다.




아래와 같이 TableViewController의 myCell 아울렛에 가져온 Table View Cell을 연결합니다.


사용자 삽입 이미지
라이브러리 윈도우에서 Image View, Label, Rounded Rect Button, Text View를 드래그 해서 Table View Cell에 가져다 놓습니다.



Cell의 높이를 늘리고 각각의 컨트롤들을 아래와 같이 배치합니다. 적당한 이미지를 Xcode 좌측의 Groups & Files 아래의 Resources 그룹으로 드래그해서 프로젝트로 가져옵니다. Image View의 Image 속성에서 해당 이미지를 선택합니다. 입력이 되지 않도록 Text View의 Editable 속성의 체크를 해제합니다.
사용자 삽입 이미지

버튼 클릭시 사파리에서 해당 URL로 이동하기 위해 Text View Controller의 goMyBlog IBAction과 연결합니다.


4. 확인
Xcode에서 Build & Go를 클릭하면 시뮬레이터에서 아래와 같이 확인하실 수 있습니다.
SDK 3을 대충 보기만 하고 처음 사용해 보았는데 곳곳에 바뀐 부분이 눈에 뛰네요. SDK 3도 처음이고 오랫만에 포스팅이라 틀린 부분이 있는지도 모르겠습니다.

'iOS' 카테고리의 다른 글

아이폰 OS 4  (8) 2010.04.09
NSXMLParser로 RSS 읽어오기  (21) 2009.08.05
cocos2d 개발환경 설정  (24) 2009.04.13
iPhone SDK 3.0 beta 2  (4) 2009.04.05
UITableView의 메모리 누수 현상  (4) 2009.02.05
AND

사용자 삽입 이미지
이번 포스팅에서는 인터페이스 빌더 3.1을 둘러 보겠습니다. 역시나 이전 Xcode 둘러보기와 같이 두서없고 깊이 없이 둘러만 보겠습니다.



1. XIB 파일
처음 Xcoe3.1을 실행하고 당황스러운 부분은 더블클릭해서 인터페이스 빌더를 실행해야 할 nib 파일이 없어졌다는 것입니다.

사용자 삽입 이미지
그대신 Resources 그룹을 보면 MainMenu.xib가 새로 추가되어 있습니다. 이 MainMenu.xib를 더블클릭 하면 이전과 같이 인터페이스 빌더가 실행됩니다.


아래는 텍스트 에디터에서 열어본 *.xib 파일의 내용입니다.

사용자 삽입 이미지

요즘 많은 툴들에서 사용하는 XML 포맷으로 되어 있지만, 애플에서는 절대 직접 수정하지 말라고 권하고 있습니다. xib 파일은 기본 3,305 라인이기 때문에 그다지 수정할 생각은 들지 않습니다. xib 파일은 아마 윈도우의 rc 파일의 역활을 하는 것 같습니다.

nib 파일은 Xcode에서는 찾을 수 없지만(하위 호환을 위해서 NIB Files 그룹이 존재 하는 것 같습니다) 빌드 후에는 패키지내에 포함됩니다.


2. XIB 윈도우
2.5 버젼과는 달리 Instance, Classes, Images, Sounds, Nib등의 탭이 없어지고, 이전의 Instance에 해당되는 부분만 있습니다. Application과 Font Manager가 추가되었습니다.

사용자 삽입 이미지

3. 라이브러리 윈도우

이전의 팔레트가 라이브러리 윈도우로 변경되었습니다. 이제 UI 컨트롤, 인스턴스를 추가하는 작업에 라이브러리 윈도우를 사용합니다. 이전과 같이 NIB 윈도우에서 Classes 탭을 이용하여 subclass를 추가할 수는 없는 것 같습니다. 하지만 인스턴스를 클래스(파일)로 만드는 것은 여전히 가능합니다. 아래가 라이브러리 윈도우 입니다.

사용자 삽입 이미지

Cocoa 어플리케이션에서 사용하는 오브젝트와 컨트롤들은 Cocoa 그룹 하단에 다시 세부로 분류되어 잘 정리 되어 있습니다. 지금은 용도를 알수 없는 아이템들이 많고 이전과 비교하여 추가된 부분이 많기 되었기 때문에 기회가 되면 따로 각 아이템의 용도를 공부한 후에 설명해야 될 것 같습니다.

사용자 삽입 이미지
아직 각 아이템들의 명칭을 잘 몰라서 라이브러리 윈도우에서 우클릭을 하면 나오는 보기 옵션에서 'View Icons And Lables'를 선택하여 이름도 같이 볼 수 있도록 선택해 놓았습니다.


4. AppController 인스턴스 생성
우선 AppController 인스턴스와 클래스 파일을 만들어 보겠습니다. 라이브러리 윈도우에서 Cocoa/Objecrts & Controllers(/Controllers) 에서 Object 아이템을 드래그해서 아래와 같이 xib 도큐멘트 윈도우로 가져다 놓습니다.
사용자 삽입 이미지
새로 만들어진 Object가 선택된 상태에서 Shift+Command+i를 클릭하여 인스펙트 윈도우를 오픈합니다. 아래와 같이 Identity 항목을 선택한 다음 Class에 AppController를 입력합니다. 그리고 Action과 Outlet 하단의 [+] 버튼을 클릭하여 아래와 같이 changeTextLabel과 textLabel, userInput을 각각 추가하였습니다.

사용자 삽입 이미지


5. Outlet, Action 연결
라이브러리 윈도우에서 Label, TextField, Push Button을 윈도우로 드래그 해서 아래와 같이 배치하였습니다.
사용자 삽입 이미지
연결은 오브젝트를 컨트롤 키와 함께 클릭하여 연결될 오브젝트로 드래그 하는 이전과 동일한 방법도 가능합니다. 이전과 다른 점은 인스펙터의 연결창이 오픈되는 것이 아니라 아래와 같이 연결될 Action 또는 Outlet의 목록만 출력됩니다.
사용자 삽입 이미지 사용자 삽입 이미지

인터페이스 빌더 3에서는 더욱 편리한 방법이 있습니다. xib 도큐먼트 윈도우에서 AppController에 마우스 우클릭을 하면 아래와 같이 연결 판넬이 오픈됩니다.

사용자 삽입 이미지

textLabel의 우측에 원모양의 아이콘을 드래그 하여 Label에 연결합니다. 동일한 방법으로 userInput을 텍스트필드에 연결하고 Received Action의 changeTextLabel을 Set 버튼에 연결합니다.
  
사용자 삽입 이미지

모두 연결된 모습입니다. 해당 항목 앞의 x 아이콘을 클릭하면 연결이 삭제됩니다.
사용자 삽입 이미지

인스펙트 윈도우의 연결 항목에서도 현재 상태를 확인할 수 있습니다. 또한 이곳에서도 해당 아이템 우측의 원모양 아이콘을 드래그하여 해당 오브젝트와 연결 또는 x를 클릭하여 삭제 할 수 있습니다.
사용자 삽입 이미지


6. 소스코드와 연결
1) 클래스 파일 생성
이제 AppController 인스턴스의 클래스 파일을 만들어 보겠습니다. AppController 인스턴스를 선택한 상태에서 인터페이스 빌더 메뉴의 File/Write Class Files...을 클릭합니다. 아래의 창이 뜨면 Save 버튼을 클릭하여 저장합니다.
사용자 삽입 이미지
생성된 파일의 프로젝트에 포함시키기 위하여 체크를 하고 Add 버튼을 클릭합니다.
사용자 삽입 이미지
2) 슈퍼클래스 지정
Xcode에서 보면 AppController 클래스 파일을 확인할 수 있습니다. 인터페이스 빌더에서 위와 같이 생성한 클래스들은 헤더파일에서 슈퍼클래스를 지정해 주어야 합니다. 오랜지 색으로 되어 있는 부분을 NSObject로 변경하고 저장합니다.

#import <Cocoa/Cocoa.h>

@interface AppController : /* Specify a superclass (eg: NSObject or NSView) */ {
    IBOutlet NSTextField *textLabel;
    IBOutlet NSTextField *userInput;
}
- (IBAction)changeTextLabel:(id)sender;
@end

3) 코드와 동기화
이제 인스펙터에서 다시 확인하면 아래의 붉은색 사각형 부분과 같이 AppController.h라 바가 생겨 Xcode의 AppController 클래스와 연결되어 있음을 알려줍니다.

사용자 삽입 이미지

이전에는 소스코드에 변경사항이 생기면 Xcode에서 해당 클래스의 헤더파일을 드래그 하여 인터페이스 빌더의 nib 도큐먼트로 드랍 해서 변경사항을 적용시켜 주어야 했습니다. 하지만 이제는 Xcode에서 소스코드에 변경사항이 생기고 저장이 되면 인터페이스 빌더에 바로 반영이 되어 매우 편리합니다.

Xcode에서 미리 클래스 파일을 만들어 놓고 인터페이스 빌더에서 해당 클래스를 선택하는 것도 가능합니다.
사용자 삽입 이미지


AppContorller의 인스펙터에서 새롭게 Outlet을 추가해 보았습니다.
사용자 삽입 이미지

위와 같이 AppController.h의 상단에 MainMenu.xib 바 밑에 추가되기 때문에, 헤더파일에 반영이 되지 않은 것을 쉽게 확인할 수 있습니다. 소스코드에서 아래와 같이 myOutlet1을 추가한 후에 저장해 보았습니다.

#import <Cocoa/Cocoa.h>

@interface AppController : NSObject {
    IBOutlet NSTextField *textLabel;
    IBOutlet NSTextField *userInput;
    IBOutlet NSTextField *myOutlet1;
}
- (IBAction)changeTextLabel:(id)sender;
@end

사용자 삽입 이미지
다시 인터페이스 빌더의 AppController의 인스펙터에서 확인하면 좌측과 같이 변경된 사항이 자동으로 반영되어 있습니다.




7. 기타
1) Effect
눈에 뛰는 것은 인스펙터에 'Effect' 항목이 추가되었다는 것입니다. 코어에니메이션을 이용하여 사용자 UI에 화려한 효과를 주는 것으로 보입니다. 아래는 효과를 선택하는 화면이며,  보시는 바와 같이 다양한 효과가 있습니다. 재미있는 것이 많을 것 같은데 몇가지만 확인해 보았습니다.

사용자 삽입 이미지

코어에니메이션을 사용하기 위해서는 Content View를 체크해야 합니다. Context Filters에서 Color Invert와 Gaussian Blur 필터를 추가해 보았습니다.

사용자 삽입 이미지

* Color Invert + Gaussian Blur 필터 적용
사용자 삽입 이미지

* Color Invert 필터 적용
사용자 삽입 이미지

2) 크기조절과 정렬
사용자 삽입 이미지
윈도우 확대시에 컨트롤들의 위치와 크기를 조절하는 부분을 움직이는 gif로 만들어 보았습니다. (실제로는 훨씬 부드럽습니다)

하얀색으로 된 부분이 윈도우고 붉은색이 해당 콘트롤입니다. 윈도우 확대시 변화되는 모습을 시각적으로 보여 줍니다.


사용자 삽입 이미지
정렬 기능이 인스펙터에 추가되었습니다. Alignment 항목은 다른 오브젝트와 같이 선택되었을 때 적용이 가능합니다.

Placement는 가로 또는 세로로 정중앙에 위치하게 합니다.



Xcode와 마찬가지로 인터페이스 빌더 3.1에도 재미있고 신기한 기능들이 많이 추가 된 것 같습니다. 잠시 사용해 본 것이라 틀린 내용들이 있을 수 있습니다. 알려 주시면 수정하겠습니다.

이전 인터페이스 빌더를 보면 독특한 방식은 인상 깊었지만 무엇인지 약간 부족한 느낌을 받았습니다. 하지만 이번 3.1버젼은 비록 짧은 시간 일부분만 확인해 보았지만, 안정적이고 완성도가 많이 높아졌다는 느낌을 받았습니다.

'Xcode 3' 카테고리의 다른 글

OS X 어플리케이션 개발을 위한 Ruby - MacRuby  (3) 2008.10.23
Cocoa-Ruby 어플리케이션  (6) 2008.08.20
Xcode 3 둘러보기(3) - 기타  (6) 2008.08.14
Xcode 3 둘러보기(1) - Xcode  (10) 2008.07.29
Xcode 3 다운로드 및 설치  (2) 2008.07.29
AND

WebKit에 포함된 WebView를 이용하여 초 간단한 웹브라우저를 만들어 보겠습니다.
(프로젝트 생성과 인터페이스 빌더에서 작업은 이전에 많이 다루었기 때문에 여기서는 자세한 설명은 생략하였습니다. 'Cocoa 따라해보기' 분류의 초기 포스트에 관련된 설명이 있습니다.)

0. WebKit
WebKit은 애플의 대표적인 오픈소스 프로젝트 중에 하나 입니다. 대표적인 사파리뿐만 아니라 아도비 에어, 아이폰, 노키아의 폰부라우져, 구글 안드로이드 플랫폼등 여러 프로젝트에서 사용되었습니다. (출처:WikiPedia/WebKit)

Webkit은 KDE의 웹브라우져 퀀커러의 엔진인 KHTML과 KJS에서 시작한 발전해 왔습니다. 주개발자는 Dave Hyatt로 넷스케이프에서 모질라를 개발하다 2002년에 애플에 합류해 현재 사파리, Webkit팀의 책임자로 있는 것으로 보입니다. 초기 파이어폭스, Camino와 탭브라우징, XBL/XUL을 만들고 W3C에도 많은 영향력이 있는 것 같습니다.

Webkit에 대한 상세한 정보는 WebKit 홈페이지에서 확인하실 수 있습니다.

1. WebKit 프레임워크 추가
Xcode의 New Project에서 Cocoa Application으로 프로젝트를 하나 만듭니다. WebKit 프레임워크를 추가하기 위하여 Groups & Files에서 우클릭하여 Add > Existing Frameworks를 선택합니다.

사용자 삽입 이미지

사용자 삽입 이미지
선택창에서 /System/Library/FrameWorks에 있는 WebKit.framework를 선택합니다. 좌측과 같이 WebKit.framework가 포함되어 있는 것을 확인할 수 있습니다.


2. 소스코드 편집

AppController이란 이름으로 Objective-C 클래스를 생성합니다. 아래와 같이 입력합니다.

1) AppController.h
#import <Cocoa/Cocoa.h>

@class WebView;

@interface AppController : NSObject {
    IBOutlet WebView *webView;
    IBOutlet NSTextField *inputUrl;
}

- (IBAction)goURL:(id)sender;

@end

2) AppController.m
#import "AppController.h"
#import <WebKit/WebKit.h>

@implementation AppController

-(void)awakeFromNib
{
    /* 기본 주소 설정 */
     NSURL *url = [NSURL URLWithString:@"http://www.cocoadev.co.kr"];
    [[webView mainFrame] loadRequest:[NSURLRequest requestWithURL:url]];
}

- (IBAction)goURL:(id)sender
{
    /* 사용자 입력 주소 Load */
    [[webView mainFrame] loadRequest:[NSURLRequest requestWithURL:
        [NSURL URLWithString:[sender stringValue]]]];
}
@end

3. 인터페이스 빌더
Xcode의 AppController.h를 MainMenu.nib로 드래그 해서 가져다 놓습니다. Classes 항목의 AppController를 우클릭하여 Instantiate AppController를 선택하여 인스턴스를 생성합니다.

팔레트에서 NSTextField와 CustomView를 드래그해서 아래와 같이 윈도우에 배치합니다.
사용자 삽입 이미지

1) NSTextField 속성 변경
사용자 삽입 이미지
NSTextFiled는 윈도우 크기가 변경되더라도 위치를 유지하면서 가로 크기만 변하도록 설정하겠습니다.

NSTextField의 속성창중 Size메뉴(단축키: Command+3)에서 AutoSizing 속성을 좌측과 같이 변경합니다.


2) CutomView 속성 변경
사용자 삽입 이미지
웹컨텐츠를 보여주는 View는 윈도우의 크기에 맞춰 변환되도록 AutoSizing을 좌측과 같이 설정합니다.

그리고 Custom Class 항목(단축키: Command+5)에서 WebView를 선택합니다.


3) AppController와 연결
이제 AppController의 inputUrl을 NSTextField로 webView를 webView로 연결합니다.  그리고 Action에서 NSTextField는 goURL로 연결합니다.

사용자 삽입 이미지

4. 테스트 or More..
빌드 후에 실행하면 아래와 같습니다. 입력창에 URL을 입력하면 다른 사이트로 연결됩니다.
사용자 삽입 이미지

More...
Developer/Examples/WebKit/MiniBrowser에 히스토리가 저장되고 앞으로, 뒤로 가기, 리로드, 정지, 폰트 확대/축소 등의 기능이 추가된 예제가 있습니다. 그외 ADC의 WebKit과 관련된 아래의 문서들이 도움이 되실 것입니다.
AND

이번에는 Core Data를 이용해 간단한 할일(Todo) 어플리케이션을 만들어 보겠습니다. Core Data는 객체의 연결, 저장, 불러오기등의 복잡한 데이터 관련 작업을 쉽게 관리할 수 있도록 해줍니다. 많은 부분이 자동화 되어 있어 개발자가 해야할 작업은 매우 적습니다. 실제 이번 예제에서도 모델링 툴과 인터페이스 빌더를 이용하여 객체를 생성하고 연결하는 작업만 하고 소스코드에서는 작업을 하지 않을 것입니다.

이 예제는 ADC에서 제공하는 Building a Sample Core Data Application 동영상 강좌를 참고하였습니다. 동영상으로 되어 있기 때문에 이 포스팅 보다 따라 해보기가 매우 쉬우실 것입니다. Core Data에 관한 상세한 내용은 Core Data Programming Guide 문서를 참조하시기 바랍니다.

1. 프로젝트 생성
New Project에서 Core Data Document-based Application을 선택합니다. Core Data Document-based Application은 Document와 연결된 datamodel을 생성해주고, 파일 Open/Save와 MDI를 지원합니다.

Project Name을  'MyToDoList'로 입력하고 [Finish] 버튼을 클릭합니다.

사용자 삽입 이미지
이제 Xcode에서 좌측과 같이  Models 밑에MyDocument.xcdatamodel이 생성되어 있음을 확인할 수 있습니다.


2. 데이터 모델링 툴
작업을 위해 MyDocument.xdatamodel을 더블클릭하면, 아래와 같은 데이터 모델링 툴이 오픈됩니다.
사용자 삽입 이미지

일반적인 모델링 툴과 사용방법이 유사합니다. 상단 좌측에서 객체를 생성하고 우측의 창에서 객체들의 각각의 속성을 관리합니다. 우측에 현재 'No Selection'로 표시된 영역에서 선택된 값들의 세부 속성을 설정합니다. 하단에는 생성된 객체들이 그래픽한 다이어그램으로 표시되어 쉽게 속성과 연결을 확인할 수 있습니다.

3. 오브젝트 생성
Todo 어플리케이션은 작업들의 목록과 각 작업들 아래 세부 작업들을 관리할 수 있도록 만들려고 합니다. 이를 위해 작업(Work)와 세부작업(Todo)의 두개의 객체(NSManagedObject)를 생성하겠습니다. 좌측 상단의 Entity필드가 있는 목록 하단에 위치한 +버튼을 두번 클릭하여 2개의 객체를 생성 합니다.

사용자 삽입 이미지
좌측과 같이 Entity를 Work와 Todo로 변경합니다.


4. 항목 설정
사용자 삽입 이미지
이제 각각의 객체에 항목을 추가하겠습니다. Work 객체를 선택한 상태에서 우측에 있는 Property 목록 하단의 + 버튼을 클릭합니다. 나오는 메뉴중에서 좌측과 같이 Add Attribute를 클릭합니다.

생성된 항목의 속성 창에서 Name을 'title'로 Default Value를 '새작업'으로 입력합니다.
사용자 삽입 이미지

이제 Todo를 선택하고 위와 같은 방법으로 세개의 항목을 추가하고 아래와 같이 속성을 설정합니다.
사용자 삽입 이미지

5. 연결(RelationShip) 설정

사용자 삽입 이미지
Work가 선택된 상태에서 좌측과 같이 + 버튼의 Add RelationShip을 클릭하여 새로운 연결을 생성합니다.



아래와 같이 속성에서 이름을 todo(소문자로 시작해야 합니다.)로 설정하고 Destination에서 연결될 객체를 Todo를 선택합니다.
사용자 삽입 이미지

이제 Todo를 선택하고 위와 같은 방법으로 RelationShip을 추가합니다. Destination은 Work 객체로 Inverse를 todo로 선택하여 상호 참조할 수 있도록 해줍니다.
사용자 삽입 이미지

이제 다시 Work 객체의 todo에서 아래와 같이 설정합니다. 하나의 Work에 여러개의 Todo가 연결될 수 있도록 To-Many Relationship에 체크합니다.
사용자 삽입 이미지


6. 확인
사용자 삽입 이미지
하단을 보시면 좌측과 같이 지금까지 작업한 내용을 쉽게 확인할 수 있습니다.

Work와 Todo는 연결되어 있고 Todo쪽의 화살표가 이중으로 표시되어 있습니다. 이는 Work와 Todo가 1:N으로 연결(To-Many Relationship)되어 있슴을 나타냅니다. 하나의 Work에 대하여 여러개의 Todo가 올수 있습니다.

7. ArrayController 생성

이제부터 인터페이스 빌더에서 작업을 작업을 해보겠습니다. MyDocument.nib를 더블클릭하여 인터페이스 빌더를 오픈합니다.

사용자 삽입 이미지
좌측과 같이 팔레트 윈도우에서 >> 버튼을 클릭하고 나오는 메뉴에서 Controllers를 클릭합니다.

좌측 하단의 NSArrayController를 드래그하여 인스턴스 창에 가져다 놓습니다. 한번 더 반복합니다.


사용자 삽입 이미지
가져온 컨트롤러의 이름을 위와 같이 WorkController와 TodoController로 변경합니다.

1) WorkController 설정
사용자 삽입 이미지
WorkController 속성중 Attribute에서 좌측과 같이 Mode를 Entity로 체크하고 이름을 Work로 입력합니다.
Automatically prepares content에 체크합니다.



사용자 삽입 이미지
Binding 항목 하단에 있는 Parameters의 ManagedObjectContext를 좌측과 같이 설정합니다.





2) TodoController 설정
사용자 삽입 이미지
TodoController도 EntityName만 Todo로 입력하고 위와 동일하게 설정합니다.

그외에 Todo가 Work에 종속성을 가지도록 한가지 작업을 더 합니다. Binding 항목의 Controller Content 밑의 ContentSet의 항목을 좌측과 같이 설정합니다.




8. 사용자 인터페이스
윈도우에 NSTableView 2개와 NSTextField, NSDataPicker, NSSearchField, NSButton들을 아래와 같이 배치합니다.

각각의 테이블뷰 Inspector의 Attribute에서 Columns를 1과 3으로 설정하고 각 컬럼을 더블클릭하여 '작업', '완료', '할일', '예정일'로 이름을 변경합니다.
사용자 삽입 이미지

9. Work, Todo 테이블컬럼 설정
1) Work - 작업 컬럼(NSTableColumn)
작업 컬럼을 더블클릭하여 아래와 같이 설정합니다.
사용자 삽입 이미지

2) Todo - 완료 컬럼(NSTableColumn)
Todo 컬럼들은 직접입력을 막기 위해 모두 Attribute에서 Editable의 체크를 해제합니다.  Binding에서 각각의 컬럼들을 아래와 같이 설정합니다.
사용자 삽입 이미지

3) Todo - 할일 컬럼(NSTableColumn)
사용자 삽입 이미지

4) Todo - 예정일 컬럼(NSTableColumn)
사용자 삽입 이미지

5) 작업:(NSTextField)
사용자 삽입 이미지

10. 기타 컨트롤 설정
1) 완료 예정일 :(NSDatePicker)
사용자 삽입 이미지

2) 완료:(NSButton)
사용자 삽입 이미지

3) 검색(NSSearchField)
Binding에서 Search/predicate에서 아래와 같이 설정합니다. Predicate Format:에서  key가 'memo'로 변경되었습니다.
사용자 삽입 이미지

4) 동작 버튼
이제 각각의 버튼을 컨트롤키와 함께 드래그해서 Controller에 연결해서 아래와 같이 설정합니다. 좌측의 두 버튼은 WorkController에 우측의 두 버튼은 TodoController에 연결하고, '+' 버튼은 add:에 '-' 버튼은 remove:에 연결합니다.
사용자 삽입 이미지

11. 완료
이제 모든 작업이 완료되었습니다. 빌드를하고 테스트를 해봅니다. 좌측에서 작업을 먼저 입력하시고 작업별로 할일을 등록하시면 됩니다.

사용자 삽입 이미지

사용자 삽입 이미지

마우스 클릭과 속성 설정만으로 등록, 변경, 검색, 저장, 불러오기등이 동작하는 간단한 ToDo 어플리케이션을 만들어 보았습니다. 프로젝트 파일은 아래의 아이콘을 클릭하여 다운로드 받으실 수 있습니다.

'Xcode 2 > Cocoa 따라해보기' 카테고리의 다른 글

간단한 웹브라우저 만들기  (0) 2008.04.02
1.10 기본 콘트롤 사용법 (2)  (0) 2008.02.13
1.10 기본 콘트롤 사용법 (1)  (2) 2008.02.03
1.8 간단한 슈팅게임 (2)  (2) 2007.10.03
1.8 간단한 슈팅게임 (1)  (4) 2007.08.14
AND

이전 포스트에 이어서 프로그램을 완성해 보겠습니다. 이전의 소스에서 아래의 청색으로 된 부분을 추가합니다.

1. AppController.h 수정

#import <Cocoa/Cocoa.h>

@interface AppController : NSObject {
    NSMutableString *curString;
    NSString* dataArray[3][2];
   
    IBOutlet NSTextField* displayString;
    IBOutlet NSTableView* wordList;
    IBOutlet NSMatrix* subjectMatrix;
    IBOutlet NSComboBox* adverbSelecter;
    IBOutlet NSColorWell* colorWell;
    IBOutlet NSSlider* alignSlider;
    IBOutlet NSButton* notChecker;
    IBOutlet NSProgressIndicator* progressBar;   
}

- (int)numberOfRowsInTableView:(NSTableView *)tableView;
- (id)tableView:(NSTableView *)tableView
    objectValueForTableColumn:(NSTableColumn *)tableColumn
            row:(int)row;

- (IBAction)setDisplayText:(id)sender;
- (IBAction)adverbSelecterChanged:(id)sender;
- (IBAction)alignSliderChanged:(id)sender;
- (IBAction)notCheckerChanged:(id)sender;
- (IBAction)subjectMatrixChanged:(id)sender;
- (IBAction)colorWellChanged:(id)sender;
- (IBAction)wordListChanged:(id)sender;
@end


2. 인터페이스 빌더에서 수정

변경한 소스를 저장한 후에 변경된 사항을 인터페이스 빌더에 적용하기 위해서 다시 AppController.h 파일아이콘을 드래그 하여 인터페이스 빌더의 MainMenu.nib에 놓습니다.

사용자 삽입 이미지
좌측과 같이 윈도우의 TableView중에 목적어 컬럼을 선택합니다. 속성창을 열어 Identifier 항목을 1로 설정합니다.

이와 동일하게 동사 컬럼의 Identifier는 2로 설정합니다.




사용자 삽입 이미지
AppController에서 새로 추가한 메소드인 wordListChanged르 TableView에 연결합니다.

TableView를 선택하고 Control키를 클릭한 채 마우스로 드래그 하여 AppController 인스턴스에 가져다 놓습니다. 좌측의 속성 화면에서 wordListChanged를 선택하고 연결합니다.

(연결이 어려우신 분들은 이전 포스트를 참조하세요.)


3. AppController.m 수정

#import "AppController.h"

@implementation AppController

- (id)init
{
    self = [super init];
   
    curString = [[NSMutableString alloc] init];

    /* 목적어 데이터 설정 */

    dataArray[0][0] = [[NSString alloc] initWithUTF8String:"철수를"];
    dataArray[1][0] = [[NSString alloc] initWithUTF8String:"영희를"];
    dataArray[2][0] = [[NSString alloc] initWithUTF8String:"바둑이를"];
   
    /* 동사 데이터 설정 */
    dataArray[0][1] = [[NSString alloc] initWithUTF8String:"사랑했다"];

    dataArray[1][1] = [[NSString alloc] initWithUTF8String:"싫어했다"];
    dataArray[2][1] = [[NSString alloc] initWithUTF8String:"때렸다"];
   
    return self;
}

- (void)awakeFromNib
{
}

- (void)dealloc
{
    int i, j;
   
    /* 할당된 오브젝트 해제 */
    for(i = 0; i < 3; i++)
    {
        for(j = 0; j < 2; j++)
        {
            [dataArray[i][j] dealloc];
        }
    }
   
    [curString dealloc];
    [super dealloc];
}

- (IBAction)setDisplayText:(id)sender
{
    int pos = [wordList selectedRow]; // 테이블뷰에서 현재 선택된 열
    if(pos == -1) // 없으면 실행하지 않는다
        return;
   
    /** 조건에 따른 문자열 설정 */
    if([[subjectMatrix selectedCell] tag] == 0)
        [curString setString:[NSString stringWithUTF8String:"나는 "]];
    else
        [curString setString:[NSString stringWithUTF8String:"너는 "]];
   
    [curString appendString:dataArray[pos][0]];
    [curString appendString:@" "];
    [curString appendString: [adverbSelecter stringValue]];
       
    if([notChecker state] == NSOnState)
        [curString appendString:[NSString stringWithUTF8String:" 안 "]];
   
    [curString appendString:dataArray[pos][1]];
   
    /* 색상 설정 */
    [displayString setTextColor:[colorWell color]];
   
    /* 정렬 설정 */
    int align = [alignSlider intValue];

    if(align == 0)
        [displayString setAlignment:NSLeftTextAlignment];
    else if(align == 50)
        [displayString setAlignment:NSCenterTextAlignment];
    else
        [displayString setAlignment:NSRightTextAlignment];
   
    /* 텍스트 출력 */
    [displayString setStringValue:curString];
   
    /* 진행 상태바 에니메이션 중지 */
    [progressBar stopAnimation:self];

}

- (IBAction)adverbSelecterChanged:(id)sender
{
    /* 변경시 진행 상태바 에니메이션 시작 */
    [progressBar startAnimation:self];
}

- (IBAction)alignSliderChanged:(id)sender
{
    [progressBar startAnimation:self];
}

- (IBAction)notCheckerChanged:(id)sender
{
    [progressBar startAnimation:self];
}

- (IBAction)subjectMatrixChanged:(id)sender
{
    [progressBar startAnimation:self];
}

- (IBAction)colorWellChanged:(id)sender
{
    [progressBar startAnimation:self];
}

- (IBAction)wordListChanged:(id)sender
{
    [progressBar startAnimation:self];
}

- (int)numberOfRowsInTableView:(NSTableView *)tableView
{
    /* 테이블 열 갯수 3 반환 */
    return 3;

}

- (id)tableView:(NSTableView *)tableView
    objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
{
    int pos;

    /** 현재 설정되어야 할 TableView의 컬럼을 반환한다. */
    if([[tableColumn identifier] characterAtIndex:0] == '1')
        pos = 0;
    else
        pos = 1;
   
    return dataArray[row][pos];
}

@end


사용자 삽입 이미지
이제 빌드를 하고 실행 하시면 좌측과 같이 어플리케이션이 실행됩니다. 각각의 옵션을 선택하고 [적용] 버튼을 클릭하면 선택된 옵션에 맞추어 상단에 텍스트가 출력됩니다.

옵션에 변동이 일어나면 하단의 바가 에니메이션되며 [적용] 버튼을 클릭하여 적용이 완료되면 진행 상태바의 에니메이션이 중지 됩니다.

전체 프로젝트 소스파일은 아래를 클릭해서 다운로드 받이스면 됩니다.







AND

사용자 삽입 이미지
흔히 사용되는 컨트롤들의 간단한 사용법을 알아 보겠습니다.
만들려는 예제의 모습은 좌측과 같습니다. 각 컨트롤들의 설정에 따라서 상단 텍스트 필드의 내용을 변경하여 보여주는 샘플을 작성해 보겠습니다.

이 예제를 따라 해보기 위해서는 인터페이스 빌더의 사용법과  인스턴스 생성, 인스턴스와의 연결 등에 관한 기본 내용을 알고 있어야 합니다. 잘 이해가지 않는 분들은 이전 포스트를 확인해 보신 후에 다시 보시기 바랍니다.



1. AppController 생성

XCode에서 Cocoa Application으로 새로운 프로젝트를 생성합니다. 다시 New File에서 Objective-C Class를 선택한 후에 AppController란 이름으로 새로운 클래스를 생성합니다.

1) AppController.h 변경
이번 장에서 사용할 컨트롤들은 아래와 같습니다. 아래의 컨트롤들을 인터페이스 빌더에서 연결하기 위해 IBOutlet 변수들을 생성합니다.
NSTextField, NSTableView, NSMatrix, NSComboBox, NSColorWell, NSSlider, NSButton, NSProgressIndicator  

사용자의 조작으로 컨트롤의 상태가 변하는 알림 메시지를 받을 메소드를 생성합니다. 각 메소드는 아래와 같습니다.

- (IBAction)setDisplayText:(id)sender;
- (IBAction)adverbSelecterChanged:(id)sender;
- (IBAction)alignSliderChanged:(id)sender;
- (IBAction)notCheckerChanged:(id)sender;
- (IBAction)subjectMatrixChanged:(id)sender;
- (IBAction)colorWellChanged:(id)sender;

AppController.h
#import <Cocoa/Cocoa.h>

@interface AppController : NSObject {
    IBOutlet NSTextField* displayString;
    IBOutlet NSTableView* wordList;
    IBOutlet NSMatrix* subjectMatrix;
    IBOutlet NSComboBox* adverbSelecter;
    IBOutlet NSColorWell* colorWell;
    IBOutlet NSSlider* alignSlider;
    IBOutlet NSButton* notChecker;
    IBOutlet NSProgressIndicator* progressBar;   
}

- (int)numberOfRowsInTableView:(NSTableView *)tableView;
- (id)tableView:(NSTableView *)tableView
    objectValueForTableColumn:(NSTableColumn *)tableColumn
            row:(int)row;

- (IBAction)setDisplayText:(id)sender;
- (IBAction)adverbSelecterChanged:(id)sender;
- (IBAction)alignSliderChanged:(id)sender;
- (IBAction)notCheckerChanged:(id)sender;
- (IBAction)subjectMatrixChanged:(id)sender;
- (IBAction)colorWellChanged:(id)sender;

@end


2) AppController.m 변경

사용자 삽입 이미지
TableView는 좌측과 같이 테이블 형식으로 목록을 보여주는 컨트롤입니다. 각 셀에 데이터를 입력하기 위해서는 데이터를 등록하여 주는 dataSource를 지정해야 합니다.

여기서는  나중에 인터페이스 빌더에서 AppController를 dataSource로 등록하겠습니다. tableView의 dataSource가 되면 데이터를 요구하는 메시지에 응답하기 위해서 아래와 같은 두개의 메소드를 구현해야 합니다.

- (int)numberOfRowsInTableView:(NStableView *)tableView;
목록의 갯수를 반환합니다.

- (id)tableView:(NSTableView *)tableView
    objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row

각 셀의 데이터를 반환합니다. 이 반환된 값들로 각 셀들이 채워집니다. 셀에 데이터를 등록하는 것은 다음 포스팅에서 구현해 보겠습니다.

메시지를 처리하는 각 메소드들은 실제 기능을 구현하기 전에 정확히 동작하는지 확인하기 위해서 NSLog를 추가합니다. 소스파일 전체는 아래와 같습니다.

AppController.m
#import "AppController.h"

@implementation AppController

- (void)awakeFromNib
{
}

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

- (IBAction)setDisplayText:(id)sender
{
    NSLog(@"Button colicked");
}

- (IBAction)adverbSelecterChanged:(id)sender
{
    NSLog(@"adverbSelecter changed");
}

- (IBAction)alignSliderChanged:(id)sender
{
    NSLog(@"alignSlider Changed");
}

- (IBAction)notCheckerChanged:(id)sender
{
    NSLog(@"notChecker changed");
}

- (IBAction)subjectMatrixChanged:(id)sender
{
    NSLog(@"subject matix");
}

- (IBAction)colorWellChanged:(id)sender
{
    NSLog(@"colorWell changed");
}

- (int)numberOfRowsInTableView:(NSTableView *)tableView
{
    NSLog(@"tableView getRowCount");
    return 1;
}

- (id)tableView:(NSTableView *)tableView
    objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
{
    NSLog(@"tableView getColumnValue");
    return nil;
}

@end


2. 인터페이스 빌더에서 작업
 
1) 컨트롤 배치

윈도우에 각 컨트롤들이 배치되어야 할 모습은 아래와 같습니다.

사용자 삽입 이미지

사용자 삽입 이미지
팔레트에서 마우스 포인터를 해당 컨트롤 위에 놓으면 좌측의 빨간 화살표가 가르키는 것과 같이 컨트롤의 종류를 확인할 수 있습니다.

위의 이미지를 참조하여 팔레트에서 각각의 콘트롤들을 드래그로 가지고 와서 배치 합니다.





2) 속성 설정
각각의 콘트롤들의 속성을 변경합니다.

NSTextField
사용자 삽입 이미지
텍스트필드의 옵션을 좌측과 같이 설정합니다. 사용자의 입력은 받지 않기 때문에 Editable, Enabled를 해제합니다.


NSTableView
사용자 삽입 이미지
Attributes에서 Columns를 2로 설정합니다.
NSTableView를 더블클릭하면 좌측과 같이 푸른 테두리가  나타납니다. 이 상태에서 컬럼을 더블클릭하여 입력 모드로 변경되면 이름을 변경할 수 있습니다.

각각 목적어, 동사로 변경합니다.

NSMatrix
Rows는 1 Cols는 2로 설정하고 Mode가 'Radio'로 되어 있음을 확인합니다.

NSComboBox
사용자 삽입 이미지
좌측과 같이 Visible Items를 4로 입력하고 4개의 항목을 추가합니다.

추가는 하단의 입력필드에서 이름을 입력 후에 [+] 버튼을 클릭합니다. 삭제는 아이템을 선택한 후에 [-]버튼을 클릭합니다.



NSColorWell
변경없이 기본값을 유지합니다.

NSSlider
사용자 삽입 이미지
좌측과 같이 최소값을 0.0, 최대값을 100.0, 기본값을 50.0으로 설정합니다. 세단계만 선택되게 하기 위해 Number Of Markers를 3으로  Stop on ticks marks only를 체크 합니다.


NSButton
Type이  'CheckBox'로 되어 있음을 확인하고 Title에 '부정어로 만듭니다.'로 입력합니다.

NSProgressIndicator  
MinumRange는 0.0, MaximumRange는 100.0으로 설정하고 Indeterminate를 체크 합니다.

2) AppControll 인스턴스 생성
AppController.h를 인터페이스 빌더의 MainMenu.nib 윈도우로 드래그 해 놓습니다. Classes 항목에서 AppController를 우클릭하여 Instantiate AppController를 선택하여 인스턴스를 생성합니다.

이제 아래의 연결된 모습을 참조하여 윈도우의 각 항목들을 AppController의 IBOutlet 변수와 IBAction 메소드로 연결합니다.
사용자 삽입 이미지

각 컨트롤들은 아래의 AppController의 IBAction 메소드와 연결됩니다.
  • adverbSelecterChanged <- NSComboBox
  • alignSliderChanged <- NSSlider
  • notCheckerChanged <-NSButton (Check Box)
  • subjectMatrixChanged <- NSMatrix
  • colorWellChanged <- NSColorWell
  • setDisplayText <- NSButton (적용 Button)
 
사용자 삽입 이미지
마지막으로 위에 설명한 대로 좌측과 같이  TableView의 dataSource를 AppControll로 설정합니다.








이제 컴파일 하고 실행한 후에 각각의 콘트롤의 값과 상태를 변경하여 봅니다. 그리고 RunLog 창에서 아래와 같이 메시지를 보내고 받는지 확인합니다.

잘 동작하지 않는 분들은 아래의 샘플 파일을 다운로드 받으신 후에 비교해 보시기 바랍니다.
사용자 삽입 이미지

AND

파이프(pipe)란 그 이름과 같이 한 프로세스의 표준 출력을 다른 프로세스의 표준입력으로 연결하여 주는 프로세스간의 통신을 위한 방법입니다. 파이프는 아래와 같이 많이 사용됩니다.

사용자 삽입 이미지

ps로 현재 프로세스를 출력하는데 결과가 파이프(|)를 통해 grep의 입력으로 전달됩니다. 그래서 출력 내은 grep으로 전달되고, 현재 프로세스중 iTerm의 문자열이 있는 프로세스만 출력해줍니다.

이번장에선 파이프를 이용하여 command line, GUI의 두 어플리케이션간에 통신을 하고, 프로젝트를 또 다른 프로젝트에 임포트 시키는 프로그램을 간단히 만들어 보겠습니다.


1. getDouble 커멘트라인 툴

먼저 커메드라인 툴을 만들어 보겠습니다.

Xcode의 New Project 메뉴를 클릭하시고, 프로젝트 타입에서 Command Line Utility/Starndard Tool 을 선택합니다. 전 getDouble이란 이름으로 프로젝트를 생성하였습니다. main.c 소스파일을 열고 아래의 내용을 추가합니다.

#include <stdio.h>

int main (int argc, const char * argv[]) {
    // insert code here...
    int num;
   
    scanf("%d", &num);
    printf("%d", num * 2);
   
    return 0;
}

보시는 바와 같이 사용자로 부터 한 수를 입력 받아 2를 곱해서 출력해주는 간단한 프로그램입니다. 테스트를 위해 빌드 후에 실행 합니다.
사용자 삽입 이미지
위와 같이 Run Log 창에서 3[return]을 입력하고, 결과는 두배인 6이 출력되었습니다. 이제 프로젝트를 닫습니다.


2. testPipe GUI 어플리케이션

다시 New Project를 클릭하고 Application/cocoa application를 선택합니다. 저는 이름을 위와 같이 testPipe로 하였습니다.

New File에서 Objective-C class를 선택하시고 AppController란 이름으로 새로운 클래스를 생성합니다. 아래의 내용을 추가합니다.

1) AppController.h 파일

#import <Cocoa/Cocoa.h>

@interface AppController : NSObject {
    NSTask* task;
   
    /*
사용자로 부터 숫자를 입력받는 텍스트필드 입니다. */
    IBOutlet NSTextField* inputText;
   
    /*
입력받은 수를 처리한 결과를 보여 줄 텍스트필드 입니다. */
    IBOutlet NSTextField* outputText;
}

/* 버튼이 클릭되었을 경우에 입력 받은 값을 getDouble 을 실행시켜 넘겨 줍니다. */
- (IBAction) buttonClicked:(id) sender;

/*
getDouble로 부터 받은 결과값을 outputText에 출력합니다. */
- (void) setData: (NSNotification *) noti;

@end


2) AppController.m 파일

#import "AppController.h"

@implementation AppController

- (IBAction) buttonClicked:(id) sender
{
     /* getDouble을 실행하기 위해 NSTask를 사용합니다. NSTask는 한 어플리케이션에서 다른 어플리케이션을 서브프로세스로 실행시키고 관리할 수 있습니다. 리소스 폴더에 있는 getDouble을 실행합니다.

    */
    task = [[NSTask alloc] init];
    NSBundle* aabundle = [NSBundle mainBundle];
    NSString* appPath = [aabundle pathForResource: @"getDouble" ofType: @""];
   
    [task setLaunchPath: appPath];
   
    /*  입출력 파이프를 준비합니다. 유닉스에서는 파이프도 파일로 취급하기 때문에 NSFileHandle 오브젝트로 관리합니다.
    */
    NSPipe* inPipe = [[NSPipe alloc] init];
    NSPipe* outPipe = [[NSPipe alloc] init];
   
    NSFileHandle* inputFile = [inPipe fileHandleForWriting];
    NSFileHandle* outputFile = [outPipe fileHandleForReading];
   
    [task setStandardInput:inPipe];
    [task setStandardOutput:outPipe];
   
    [inPipe release];
    [outPipe release];
   
    /* getDouble로 부터의 출력이 종료되었을 때 호출된 함수(setData)를 설정하도록 옵저버로 등록합니다.
    */

    [[NSNotificationCenter defaultCenter]
        addObserver: self
           selector: @selector(setData:)
               name: NSFileHandleReadToEndOfFileCompletionNotification
             object: outputFile];
    
    /* 백그라운드로 파일을 읽고 종료시에 
읽은 데이터를 NSDicionary 타입의 userInfo에 NSFileHandleNotificationDataItem 키로 읽은 데이터를 넘겨줍니다.
     */

    [outputFile readToEndOfFileInBackgroundAndNotify];
   
    /* getDouble을 실행합니다. */
    [task launch];
   
    /* 사용자가 입력한 값을 파이프(inputFile)를 통해 getDouble 프로세스에 넘겨 줍니다. */
    NSString* str = [inputText stringValue];
    NSData* data = [str dataUsingEncoding:NSASCIIStringEncoding];
    [inputFile writeData: data];
   
    [inputFile closeFile];

}

- (void) setData: (NSNotification *) noti
{
    /* getDouble에서 printf로 출력한 값을 텍스트필드에 출력합니다. */
    NSDictionary* dic = [noti userInfo];
    NSData* data = [dic objectForKey: NSFileHandleNotificationDataItem];
   
    NSString* str = [[NSString alloc] initWithData: data encoding:NSASCIIStringEncoding];

    [outputText setStringValue: str];
   
    [str release];
    [task release];
}

@end


3) 인터페이스 빌더에서 작업

아래의 작업이 이해가 가시지 않는 분은 이전 포스트를 참조해주세요.

사용자 삽입 이미지
위의 소스파일을 저장하고 인터페이스빌더를 실행하고 Window에 텍스트필드 2개와 버튼하나를 좌측과 같이 위치시킵니다.




Xcode의 Groups & Files에서 AppController.h을 드래그하여 열려진 MainMenu.nib의 Instances 판넬에 놓습니다. 그리고 Classes에서 AppController를 우클릭하여 Instantiate AppController를 실행하여 인스턴스를 생성합니다.

AppController를 컨트롤키와 함께 드래그하여 좌측의 텍스트 필드를 inputText 아울렛에 우측의 텍스트 필드를 outputText 아울렛에 연결합니다. 반대로 버튼 컨트롤을 컨트롤키와 함께 AppConrtoller 인스턴스로 드래그하여  buttonClicked 액션에 연결합니다.

각각의 인스펙터에서 아래와 같이 연결되었음을 확인합니다.
사용자 삽입 이미지

 
3. getDouble 프로젝트 추가

실행 또는 배포 시에는 번들의 리소스 디렉토리에 getDouble 실행 파일을복사해 놓으시면 되지만 여기서는 getDouble 프로젝트를 testPipe 프로젝트로 임포트 시켜보겠습니다.

사용자 삽입 이미지

위와 같이 getDouble 프로젝트의 디렉토리에서 getDouble.xcodeproj 파일을 드래그 하여 Xcode(testPipe) 좌측의 Groups & Files에 Resources  하위에 놓습니다.

이제 getDouble이 변경되었을 때에도 testPipe에서 반영될 수 있도록 Dependencies에 추가합니다.
사용자 삽입 이미지
Groups & Files의 Targets 서브의 testPipe를 더블클릭하여 좌측의 info 창을 오픈합니다. 좌측 하단의 [+] 버튼을 클릭하여 getDouble을 선택하시고 좌측과 같이 추가된 것은 확인합니다.



이제 모든 작업이 완료되었습니다. getDouble이 정확히 포함되었는지 아래 이미지와 같이 세개의 붉은색 화살표 부분을 확인합니다.

사용자 삽입 이미지

testPipe를 빌드 후에 터미널에서 아래와 같이 리소스 디렉토리에 getDouble 실행파일을 확인합니다.
사용자 삽입 이미지


4. 실행 및 확인

사용자 삽입 이미지

빌드 후에 testPipe를 실행합니다. 위와 같이 3을 입력한 후에 [X 2] 버튼을 클릭하면 getDouble을 실행하고 파이프를  통해 3을 전달합니다. getDouble은 scanf를 통하여 파이프로 들어온 값에 2를 곱하고 출력합니다. 이 출력값은 다시 설정된 파이프를 통해 testPipe에서 받은 후 출력합니다.

이번 장에선 간단히 파이프에 대해서 알아 보았습니다. 파이프는 소켓처럼 네트워크를 통해서 프로세스간에 통신을 할 수는 없지만 로컬에서 사용할 수 있는 간단한 방법입니다. 직접 구현을 하지 않고도 존재하는 실행파일 또는 프로세스에서 간편한게 정보를 가져오거나 가공할 수 있는 편리한 방법을 제공합니다.

유닉스에서 간단하고 직관적으로 pipe를 사용하던 방법과 비교해 보면 cocoa의 특징을 잘 보여주는 것 같습니다.

'Xcode 2 > Tip' 카테고리의 다른 글

Xcode에서 Flex 개발하기  (2) 2007.12.08
Xcode에서 파이어폭스 플러그인 컴파일 하기  (0) 2007.12.04
Cocoa에서 MySQL 라이브러리 사용하기  (6) 2007.11.13
Xcode에 Subversion 적용하기  (6) 2007.11.10
Xcode 단축키  (2) 2007.10.26
AND

1.8.0 프로젝트 개요
이번 장에서는 NSView와 NSImage를 이용해서 간단한 슈팅게임을 만들어 보겠습니다.

사용자 삽입 이미지
좌측과 같이 배경을 출력하고, 우주선을 좌우로 조종하고, 미사일을 발사하는  작업까지 하겠습니다.

키입력에서 다중으로 입력 받을 수가 없어 이동시에 미사일을 발사하면 이동이 중지 됩니다. 이문제는 다음 장에서 해결하도록 하겠습니다.





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

1.8.2 인터페이스 빌더에서 작업
1) 서브 클래스 생성

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


사용자 삽입 이미지
StageView를 우클릭 하고 좌측과 같이 Create Files for StageView를 클릭하여 소스파일을 생성합니다.





2) NSView 생성 및 속성 설정

윈도우를 열고 팔레트의 Containers 항목에서  CustomeView를 드래그하여 아래와 같이 윈도우에 배치 합니다. View의 크기를 300X320으로 변경하고,  Custom Class 항목에서 StageView로 선택합니다.
사용자 삽입 이미지 사용자 삽입 이미지
(클릭하면 이미지가 확대됩니다.)

1.8.3 이미지 파일

1) 이미지 생성

이제 배경, 우주선, 미사일에 쓰일 이미지를 준비합니다. 배경은 상관없지만 우주선과 미사일은 윤곽선이외에는 투명색으로 보여야 하기 때문에 png를 사용했습니다.

사용자 삽입 이미지
저는 도스의 디럭스페인트 이후로는 그래픽 작업이 불가능하므로 초등학생인 아들에게 이미지를 부탁하였습니다. 그래픽툴들을 다루실 수 있는 분들은 새로 만드셔도 되고, 아니면 첨부파일에서 다운로드 받으셔서 사용하시면 됩니다.

좌측은 배경화면 이미지입니다. StageView와 같은 300X320의 크기를 가지고 있습니다.



사용자 삽입 이미지
우주선 이미지 입니다. 하얗게 보이는 부분을 투명하게 처리해야 하단의 배경화면에서 올바르게 보입니다. 이를 위해 png파일 포맷을 사용했습니다.

사용자 삽입 이미지
미사일 이미지 입니다. 위와 같이 png파일 포맷을 사용했습니다.

2) 이미지 등록

사용자 삽입 이미지
준비된 이미지 파일을 드래그하여 Xcode 좌측의  Group & Files 목록의 Resources에 가져다 놓습니다.



드래그 시 나오는 판넬에서 아래와 같이 Copy items... 항목의 체크를 확인합니다.
사용자 삽입 이미지

1.8.4 소스코드 작성

1) StageView.h 수정

#import <Cocoa/Cocoa.h>

@interface StageView : NSView
{
    NSTimer *timer;
    NSImage *backgroundImage;
   
    NSRect heroRect;
    NSImage *heroImage;

    BOOL isFire;
    NSRect missileRect;
    NSImage *missileImage;
}

- (void)processGame;
- (void)fireMissile;
- (void)keyDown:(NSEvent *)event;

@end


2) StageView.m 수정

#import "StageView.h"

#define SCREEN_WIDTH        300
#define SCREEN_HEIGHT        320

#define HERO_SPEED            3       /* 우주선 속도 */
#define MISSILE_SPEED        4       /* 총알발사 속도 */

@implementation StageView

- (id)initWithFrame:(NSRect)frameRect
{
    if ((self = [super initWithFrame:frameRect]) != nil) {
        // Add initialization code here
       
        isFire = NO;
       
        /* 배경 이미지 로드 */
        NSString* imageName = [[NSBundle mainBundle] pathForResource:@"background" ofType:@"png"];
        backgroundImage = [[NSImage alloc] initWithContentsOfFile:imageName];

        /* 우주선 이미지 로드 & 초기 좌표 설정 */
        imageName = [[NSBundle mainBundle] pathForResource:@"hero" ofType:@"png"];
        heroImage = [[NSImage alloc] initWithContentsOfFile:imageName];
        heroRect.size = [heroImage size];
        heroRect.origin = NSMakePoint((SCREEN_WIDTH - heroRect.size.width)/2, 0);
       
        /* 미사일 이미지 로드 & 초기 좌표 설정 */
        imageName = [[NSBundle mainBundle] pathForResource:@"missile" ofType:@"png"];
        missileImage = [[NSImage alloc] initWithContentsOfFile:imageName];
        missileRect.size = [missileImage size];
        missileRect.origin = NSMakePoint(heroRect.origin.x + heroRect.size.width/2 - missileRect.size.width/2, heroRect.size.height);
       
        /* 30프레임으로 타이머 설정 */
        timer = [[NSTimer scheduledTimerWithTimeInterval: (1.0f / 30.0f)
                                                  target: self
                                                selector:@selector(processGame)
                                                userInfo:self
                                                 repeats:true] retain];
    }
    return self;
}

- (void) dealloc
{
    [backgroundImage release];
    [heroImage release];
    [missileImage release];
       
    [super dealloc];
}

/* 키입력을 받기 위한 설정 */
- (BOOL)acceptsFirstResponder
{
    return YES;
}

- (void)keyDown:(NSEvent *)event
{
    int keyCode;
    
    /* 현재 눌려진 키값을 얻어 온다. */
    keyCode = [event keyCode];
   
    /* 좌측으로 이동 */
    if(keyCode == 123)
    {
        heroRect.origin.x -= HERO_SPEED;
        if(heroRect.origin.x < 0)
            heroRect.origin.x = 0;
    }

    /* 우측으로 이동 */
    if(keyCode == 124)
    {   
        heroRect.origin.x += HERO_SPEED;

        if(heroRect.origin.x >= SCREEN_WIDTH - heroRect.size.width)
            heroRect.origin.x = SCREEN_WIDTH - heroRect.size.width;
    }
   
    /* 발사(스페이스) */
    if(keyCode == 49)
    {
        [self fireMissile];
    }
}       

- (void)fireMissile
{
    if(isFire == NO)
    {
        /* 미사일이 처음 발사되었을 경우에 초기위치를 우주선 좌표로 설정 */
        missileRect.origin = NSMakePoint(heroRect.origin.x + heroRect.size.width/2 - missileRect.size.width/2, heroRect.size.height);
    }
    isFire = YES;
}

- (void)processGame
{
    if(isFire == YES)
    {
        /* 미사일이 발사중이면 y좌표를 이동 */
        missileRect.origin.y += MISSILE_SPEED;
        if(missileRect.origin.y >= SCREEN_HEIGHT)
        {
            /* 화면상단에 위치했을 경우에는 미사일을 출력하지 않는다 */
            isFire = NO;
        }
    }
   
    [self setNeedsDisplay:YES];
}

- (void)drawRect:(NSRect)rect
{
    NSRect imgRect;
    NSRect drawRect;
   
    /* 배경 이미지 출력 */
    imgRect.origin = NSZeroPoint;
    imgRect.size = [backgroundImage size];
   
    drawRect = [self bounds];
   
    [backgroundImage drawInRect:drawRect
                     fromRect:imgRect
                    operation:NSCompositeSourceOver
                     fraction:1.0];
   
    /* 우주선 출력 */
    imgRect.origin = NSZeroPoint;
    imgRect.size = [heroImage size];
   
    [heroImage drawInRect:heroRect
               fromRect:imgRect
              operation:NSCompositeSourceOver
               fraction:1.0];   
   
    if(isFire == YES)
    {
        /* 미사일 출력 */
        imgRect.origin = NSZeroPoint;
        imgRect.size = [missileImage size];
       
        [missileImage drawInRect:missileRect
                     fromRect:imgRect
                    operation:NSCompositeSourceOver
                     fraction:1.0];   
    }   
}

@end

#define HERO_SPEED            3       /* 우주선 속도 */
#define MISSILE_SPEED        4       /* 미사일발사 속도 */

한 프레임에서 우주선과 총알의 움직이는 속도를 설정합니다. 좌/우 방향키를 누르면 각각의 방향으로 우주선이 한 프레임당 3픽셀을 움직입니다. 미사일은 4픽셀씩 상단으로 이동합니다. 숫자를 바꾸어 주면 해당 오브젝트의 속도를 변경할 수 있습니다.

StageView가 생성될 때 자동으로 호출되는 - (id)initWithFrame:(NSRect)frameRect 메소드에서 이미지 파일을 로드하고 타이머를 세팅하는 작업을 합니다.

NSString* imageName = [[NSBundle mainBundle] pathForResource:@"background" ofType:@"png"];
backgroundImage = [[NSImage alloc] initWithContentsOfFile:imageName];

어플리케이션의 리소스 디렉토리에서 background.png 이미지 파일을 로드하고 NSImage를 생성 합니다. 우주선과 미사일 이미지를 차례로 로드합니다.

timer = [[NSTimer scheduledTimerWithTimeInterval: (1.0f / 30.0f)
                                                  target: self
                                                selector:@selector(processGame)
                                                userInfo:self
                                                 repeats:true] retain];

1/30초 마다 processGame 함수를 호출하도록 타이머를 설정합니다. processGame 함수에서는 [self setNeedsDisplay:YES];로 설정하여 DrawRect가 호출되어 이미지를 출력하게 합니다.
이제 컴파일을하고 실행을 합니다. 좌우 방향키로 우주선을 움직이며 스페이스키로 미사일을 발사할 수 있습니다. 미사일은 현재 한발씩만 발사 할 수 있습니다. 다음 장에서는 적기들을 만들고 움직여 보도록 하겠습니다.
AND

1.7.5 소스 수정

1) AppController.h 수정 및 MyWindow 연결

AppController.h를 오픈하여 아래와 같이 추가합니다. "/* */" 또는 "//"로 주석 처리된 부분은 생략 하셔도 됩니다.

#import <Cocoa/Cocoa.h>

/* 해당 버튼의 Tag 값 */
#define BTN_PLUS        100
#define BTN_MINUS        101
#define BTN_MULTIPLE    102
#define BTN_DIVISION    103
#define BTN_RESULT        104
#define BTN_CLEAR        105

@class MyWindow;

@interface AppController : NSObject {
    int prevOperation;       /* 사용자가 이전에 클릭한 연산 값 */
    int isClear;                   /* 연산을 클릭 후, 다시 값을 입력할 때 지원야 함을 알리는 플래그 */
   
    int totalValue;              /* 현재 계산 총 합 */
   
    IBOutlet MyWindow *myWindow;          /* 사용자 윈도우 */
    IBOutlet NSTextField *txtValue;             /* 현재/결과 값 표시 창 */
    IBOutlet NSTextField *txtOperation;      /* 현재 연산 모드 결과 표시 창 */
    IBOutlet NSTextField *txtHistory;          /* 연산 내역 표시 창 */
}

- (void)awakeFromNib;
- (void)processCalcul:(int)val;
- (void)processInput:(int)val;

- (IBAction) processCommand:(id)sender;
@end

저장 후에 Xcode의 MainMenu.nib를 더블클릭하여 인터페이스 빌더를 실행합니다. Xcode 좌측에서   AppController.h를 드래그 하여 MainMenu.nib 윈도우로 드래그해서 놓습니다.

이 작업은 이전 장에서 하였지만 변경된 AppController를 인터페이스 빌더에서 작업하기 위해 다시 한번 반복합니다. 아래와 같이 AppController의 myWindow 아울렛을 Window에 연결합니다.
사용자 삽입 이미지

MainMenu.nib를 저장 후, 인터페이스 빌더를 닫습니다.

2) MyWindow 소스파일 변경

MyWindow.h를 오픈하여 아래와 같이 추가합니다.
#import <Cocoa/Cocoa.h>

@class AppController;

@interface MyWindow : NSWindow
{
    AppController *app;
}

- (void)setAppController:(AppController *)a;
- (void)keyDown:(NSEvent *)event;
@end

MyWindow.m을 오픈하여 아래와 같이 추가 합니다. 사용자의 키입력을 처리하고 AppController에 알려줄 수 있도록 합니다.
#import "MyWindow.h"
#import "AppController.h"

@implementation MyWindow
/* AppControllerd에서 호출 */
- (void)setAppController:(AppController *)a
{
    app = a;
}

/* 키가 눌려질 경우, 자동으로 호출된다. */
- (void)keyDown:(NSEvent *)event
{
    int keyCode;
    int val = -1;
 
    /* 현재 눌려진 키값을 얻어 온다. */
    keyCode = [event keyCode];


    NSLog(@"KEY: %d", keyCode);
   
    /* 키값을 AppController에서 처리할 수 있도록 변경해 준다.
        각각의 키값은 좌측 숫자키 보드에서 누른 키값들이며, 위의 NSLog에서 확인할 수 있다. */
    if(keyCode >= 82 && keyCode <= 90) // 0~7
        val = keyCode - 82;
    else if(keyCode ==91) // 8
        val = 8;
    else if(keyCode ==92) // 9
        val = 9;
    else if(keyCode == 67) // *
        val = BTN_MULTIPLE;   
    else if(keyCode == 75) // /
        val = BTN_DIVISION;
    else if(keyCode == 69) // +
        val = BTN_PLUS;
    else if(keyCode == 78) // -
        val = BTN_MINUS;
    else if(keyCode == 71) // clear
        val = BTN_CLEAR;
    else if(keyCode == 81) // =
        val = BTN_RESULT;
   
    /* val이 세팅되어 처리해야 될 값일 경우에, AppController의 processInput에 값을 넘겨 준다. */
    if(val != -1)
        [app processInput:val];
   
    [self interpretKeyEvents:[NSArray arrayWithObject:event]];
}
@end

3) AppController.m 소스파일 변경

#import "AppController.h"
#import "MyWindow.h"

@implementation AppController
/* awakeFromNib는 nib파일이 로드된 후, 오브젝트들에게 보내지는 메세지 입니다.
    어플리케이션 실행 후, 각종 초기화 작업을 할 수 있습니다.
*/
- (void)awakeFromNib
{
    isClear = 0;
    prevOperation = 0;
    totalValue = 0;

    /* myWindow의  AppController 변수를 세팅합니다.
        myWindow는 키보드 입력시 이 AppController로 전달합니다.
    */
    [myWindow setAppController:self];
}

/* 연산자 (+, -, X, /, = )가 입력되었을 경우, 처리 합니다. */
- (void)processCalcul:(int)val
{
    /* 이전에 설정된 연산자를 처리한다. */
    if(prevOperation == BTN_PLUS)
        totalValue += [txtValue intValue];
    else if(prevOperation == BTN_MINUS)
        totalValue -= [txtValue intValue];
    else if(prevOperation == BTN_MULTIPLE)
        totalValue *= [txtValue intValue];
    else if(prevOperation == BTN_DIVISION)
        totalValue /= [txtValue intValue];
   
    /* 연산명령이 처음 수행되었을 경우에는, 결과값을 현재 입력된 값으로 설정 합니다. */
    if([[txtHistory stringValue] length] < 1)
        totalValue = [txtValue intValue];
   
    char operation;
    NSString *strTemp;
  
    /* 입력된 키에 따라 출력될 문자를 설정 합니다. */
    if(val == BTN_PLUS)
        operation = '+';
    else if(val == BTN_MINUS)
        operation = '-';
    else if(val == BTN_MULTIPLE)
        operation = '*';
    else if(val == BTN_DIVISION)
        operation = '/';
    else
        operation = '=';

   
/* 계산 History에 출력될 값으로 = 일 경우에는 결과값을 출력합니다. */
    if(val == BTN_RESULT)
        strTemp = [[NSString alloc] initWithFormat:@"= %d\n", totalValue];
    else
        strTemp = [[NSString alloc] initWithFormat:@"%c", operation];
   
   
/* clear 모드를 1로 설정해 이후 다시 숫자를 입력 하면, 이전 결과 값이 지워진 후,
        새 입력값이 나올 수 있도록 합니다.
        prevOperation에 다음 연산 작업을 위해 입력된 연산값을 저장합니다.
    */
    isClear = 1;
    prevOperation = val;
   
    /* 연산 창에 현재 연산모드를 출력합니다. */
    [txtOperation setStringValue:[NSString stringWithFormat:@"%c", operation]];

    /* 연산내역 창에 현재 연산 내역을 추가 합니다. */
    [txtHistory setStringValue:[NSString stringWithFormat:@"%@ %@ %@",
        [txtHistory stringValue],
        [txtValue stringValue],
        strTemp]];
   
   
/* 결과 창에 현재까지 계산된 결과를 보여 줍니다. */
    [txtValue setFloatValue:totalValue];
    [strTemp release];
}

/* 버튼 클릭과 사용자의 키보드 입력을 처리 합니다. */
- (void)processInput:(int)val
{
    NSString *strTemp;
    
    if(val >= 0 && val <= 9)
    {
      
/* 숫자가 입력되었을 경우, 처리 합니다. */
        if(isClear == 1)
        {
             /* 연산을 클릭하고, 처음 숫자가 입력되었을 경우 결과창을 초기화 합니다. */
            [txtValue setStringValue:@""];
            isClear = 0;
        }   
       
        /* 결과 창에 현재 입력된 값을 추가하여 출력 합니다. */ 
        strTemp = [NSString stringWithFormat:@"%@%d", [txtValue stringValue], val];
        [txtValue setStringValue:strTemp];
    }   
    else if(val == BTN_CLEAR)
    {
       /* clear 키를 입력하였을 경우, 데이터와 출력을 초기화 합니다. */
        isClear = 0;
        prevOperation = 0;
       
        [txtValue setStringValue:@""];
        [txtHistory setStringValue:@""];
        [txtOperation setStringValue:@""];
    }
    else if(val >= BTN_PLUS && val <= BTN_RESULT)
    {
       /* 연산이 입력되었을 경우에 현재 값이 있는지 확인 후 처리 합니다. */
        if([[txtValue stringValue] length] > 0)
            [self processCalcul:val];
    }
}

/* 버튼이 클릭되었을 경우에, processInput에 알려 줍니다. */
- (IBAction) processCommand:(id)sender
{
    /* 현재 선택(클릭)된 버튼의 tag값을 얻어 옵니다. */
    int val = [[sender selectedCell] tag];
   
    [self processInput:val];
}
@end

이제 모든 작업이 완료되었습니다. 빌드하고 실행시켜 계산기 프로그램을 테스트 해 봅니다.

아직 처리하지 않는 부분이 많이 있습니다. 이 부분과 몇 가지 기능들을 다음 튜토리얼을 통해 계속 수정/추가해 나가겠습니다. 이번 장부터는 소스코드를 압축하여 올립니다. 참고하실 분은 다운 받아 압축을 풀고, Xcode에서 확인하시고, 이 소스코드에는 주석이 되어 있지 않습니다.

AND

1.7.1 프로젝트 개요 및 생성

1) 개요

사용자 삽입 이미지
이전에도 간단한 계산기를 만들어 보았지만, 이번에는 연속계산이 가능하고, 키보드 입력과  몇 가지 출력을 추가한 업그레이드 된 계산기를 만들어 보겠습니다.

좌측과 같은 계산기인데 첫번째  232라고 되어있는 창에 입력한 값과 계산된 값이 출력 됩니다. 그 옆의 작은 창에 현재 계산 모드가 출력 됩니다. 그 아래의 큰 창에는 계산했었던 내용들이 보여 집니다. 입력은 숫자 키패드와 버튼으로 받습니다.

이번 튜토리알에서는 새로운 내용인 키입력 처리와 NSMatrix에 대해서 알아 보겠습니다.


2) 프로젝트 생성

Xcode를 실행하고, MyCalculator로 cocoa aplication 프로젝트를 새로 만듭니다. 메뉴에서 File/New File을 선택하여 Objective-C 클래스를 선택하고 AppController 클래스를 만듭니다.

위의 과정에 대한 상세한 설명은 이전 포스트에서 설명하였으니, 앞으로는 자세한 과정은 생략하도록 하겠습니다.

1.7.2 AppController 생성 및 변경
Xcode에서 AppController.h을 열어 아래와 같이 추가하고, 저장합니다. 소스에 대한 설명은 다음 장에서 하겠습니다.

#import <Cocoa/Cocoa.h>

@interface AppController : NSObject {
    int prevOperation;
    int isClear;
   
    int totalValue;
   
    IBOutlet NSTextField *txtValue;
    IBOutlet NSTextField *txtOperation;
    IBOutlet NSTextField *txtHistory;
}

- (IBAction) processCommand:(id)sender;
@end

1.7.3 사용자 인터페이스 편집

1) AppController 인스턴스 생성

Xcode의 MainMenu.nib를 더블클릭하여 인터페이스 빌더를 실행합니다. Xcode 좌측에서   AppController.h를 드래그 하여 MainMenu.nib 윈도우로 드래그해서 놓습니다.

사용자 삽입 이미지
Classes에서 AppController를 우클릭 한 후, 메뉴에서 Instantiate AppController를 클릭하여 인스턴스를 만듭니다.

좌측과 같이 인스턴스가 만들져 있는 것을 확인합니다.





2)  Window 컨트롤 배치 및 속성 변경

위의 Intances 창에 보이는 Window를 더블 클릭하여 윈도우를 엽니다.

사용자 삽입 이미지
팔레트에서 텍스트 필드 세개와 버튼 하나를 윈도우에 가져다 놓고 좌측과 같이 배치합니다.

처음 긴 텍스트 필드는 계산 결과 및 입력값, 우측 작은 텍스트 필드는 현재 연산자, 아래의 넓은 택스트 필드는 히스토리를 보여줍니다.

버튼 모양은 인스펙터에서 아래와 같이 설정하였습니다. 원하시는 모양을 선택하시면 됩니다.
사용자 삽입 이미지






사용자 삽입 이미지
첫번째 텍스트필드를 선택하고 인스펙터를 엽니다. 좌측과 같이 정렬을 우측으로 설정합니다. 인스펙터는 해당 오브젝트를 클릭 후에 메뉴 에서 선택 또는 shitf + commad(사과 키) + i를 동시에 누릅니다.

두번째 연산모드 텍스트 필드의 정렬을  가운데로 설정합니다.




사용자 삽입 이미지
위의 세 텍스트필드 모두, 옵션을 좌측과 같이 모두 체크를 해제 합니다. 키보드 입력을 윈도우가 모두 받기 위함입니다.


3) MSMatrix 생성

위의 작업들은 이전 튜토리얼에서 해 본 작업 들입니다. 이해가 가지 않는 분들은 이전 포스트를 확인해 주세요. 이제 NSMatrix를 사용하기 위해 새로운 작업을 해보겠습니다.

사용자 삽입 이미지
버튼을 클릭한 후, 쉬프트 + 옵션 + 마우스를 클릭한 상태에서, 버튼 우측 하단의 원을 클릭하면 좌측과 같이 하나의 원만 남습니다.

위의 상태에서 이 원을 드래그 하여 크기를 늘리면 버튼이 늘어 나는데, 4X4개의 버튼이 되게 합니다. 그리고 텍스트필드와 윈도우를 적당한 크기로 조절합니다.
 
사용자 삽입 이미지

위의 작업이 완료가 되면 좌측과 같은 모습을 보실 수 있습니다. 버튼을 더블클릭하면 텍스트를 수정 할 수 있습니다.

버튼을 더블클릭하여 아래와 같이 모든 버튼들의 텍스트를 변경합니다. 곱하기는 대문자 x를 사용합니다.
사용자 삽입 이미지







4) 버튼셀 tag 변경

이 작업은 매우 중요합니다. 아래와 같이 Tag 값을 정확히 설정하여야, 정확한 계산 결과 값을 보실 수 있습니다.

아래와 같이 0 버튼을 클릭하고 인스펙터에서 Tag값을 0으로 세팅합니다. 0~9까지의 버튼들을 모두 텍스트와 같은 숫자(1->1, 2->2, ...)로 변경합니다.
사용자 삽입 이미지

사용자 삽입 이미지
숫자를 제외한 +, -, X, /, =, C 기호들은 아래와 같이 Tag 값을 입력해 줍니다.

+->100, -->101, X->102, /->103, =->104, C->105


5) 텍스트 필드 Connection 설정

아래와 같이 텍스트필드들을 AppController의 아울렛들과 연결해 줍니다.
012

사용자 삽입 이미지
좌측과 같이 NSMatrix를 AppController의 processCommand에 연결 시킵니다.












6) 윈도우 서브클래스 생성 및 Window와 연결

이제 윈도우를 위한 서브클래스와 소스파일을 생성합니다.
사용자 삽입 이미지
MainMenu.nib파일의 Classes 윈도우에서 NSWindow의 서브클래스를 생성합니다.

NSWindow는 NSObject > NSResponder 밑에 있습니다.






사용자 삽입 이미지
NSWindow의 서브클래스 MyWindow를 우클릭하여 나오는 메뉴에서  Create Files for MyWindow를 선택하여, MyWindow.h와 MyWindow.m 파일을 생성합니다.





사용자 삽입 이미지
Window의 인스펙터를 오픈한 후, Custome Class에서 위에서 만든 MyWindow를 선택합니다.





1.7.4 윈도우, 메뉴, 아이콘 설정

1) 윈도우 설정

사용자 삽입 이미지
왼쪽과 같이 윈도우 타이틀을 MyCalculator로 변경합니다.

Title bar controls에서 Zoom (and resize)를 해제 하여 사용자가 윈도우의 크기를 변경 할 수 없게하며, 우측 하단에 나오는 사이즈 변경 탭이 사라집니다.



2) 메뉴 설정

사용자 삽입 이미지
MainMenu를 클릭하여 좌측과 같이 메뉴 부분에서 New Application으로 되어 있는 부분을
MyCalculator로 변경합니다.










3) 아이콘 설정

이전 튜토리얼 SimpleViewer 이미지뷰어 (2) 1.6.4 어플리케이션 다듬기에서 4) 아이콘 변경을 참조하여, 원하는 아이콘으로 변경합니다.

저는 계산기 어플리케이션 자체를 아이콘으로 만들었습니다. shift + command + 4를 동시에 누르면 캡쳐할 범위를 선택할 수 잇는 십자모양의 커서가 나옵니다. 이 커서를 계산기로 이동하여 스페이스를 누르면 해당 윈도우만 캡쳐할 수 있습니다.

위의 튜토리얼에서 3) About SimpleViewer 판넬 변경을 참조하여, 판넬부분도 변경하여 줍니다. 빌드 후 어플리케이션을 실행시킵니다. 메뉴에서 About MyCalculator를 클릭하여 아래와 같이 변경사항을 확인합니다.
사용자 삽입 이미지

이제 계산기 어플리케이션의 겉모습이 완성되어습니다. 다음 장에서는 실제로 계산기가 동작하도록 소스코드를 작성해 보겠습니다.

개인적으로 일이 있어 오랫만에 포스트를 올립니다. 저도 오랫만에 Xcode를 실행해 보니 왠지 낯서네요. 꾸준히 올릴 수 있도록 하겠습니다.


AND

1.5.0  프로젝트 개요
이번에는 NSView를 이용하여 도형들과 텍스트를 출력하는 예제를 만들어 보겠습니다. 아래와 같이 각각의 버튼들을 클릭하면 각각의 내용들을 출력하는 간단한 프로그램 입니다.
사용자 삽입 이미지
사용자 삽입 이미지
사용자 삽입 이미지
사용자 삽입 이미지






1.5.1  프로젝트 생성

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

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

1.5.2 인터페이스 빌더에서 작업
1) NSView 서브클래스 만들기

사용자 삽입 이미지
Xcode에서 MainMenu.nib를 더블클릭하여 인터페이스 빌더를 실행합니다. 좌측과 같이 Classes 에서 NSObject > NSResponder > NSview를 우클릭(또는 control + 클릭) 하여 메뉴에서 Subclass NSView를 선택 합니다.


사용자 삽입 이미지
MyView라는 서브 클랙스가 생성됩니다. 여기서는 MyView라는 이름을 사용하지만, 용도에 맞는 이름으로 변경하셔도 됩니다.







사용자 삽입 이미지
다음은 MyView에 그리기 명려을 처리할 메소드를 추가합니다. 좌측과 같이 MyView를 우클릭 하여 Add Action to MyView를 선택 합니다.




 
사용자 삽입 이미지
좌측과 같이 기본으로 만들어진 myAction을 drawItem으로 변경합니다.







사용자 삽입 이미지
이제 소스파일을 생성하기 위해 MyView 클래스를 다시 우클릭하여 Create Files for MyView를 선택합니다.

[Choose] 버튼을 클릭하여 MyView.h와 MyView.m 파일을 생성합니다.



2) NSView 속성 설정
 
이제는 NSView와 버튼들을 배치하고 속성들을 설정해 보겠습니다. 아래와 같이 윈도우를 열고 팔레트에서 여섯번째 메뉴인 Containers를 선택하고, 윈도우로 CustomView를 드래그 하여 가지고 옵니다.
사용자 삽입 이미지

사용자 삽입 이미지
윈도우에서 CustomView가 선택된 상태에서 [shift + command + i]를 눌러 인스펙터를 오픈한 후, 상단에서 Custom Class를 선택합니다.

또는 [command + 5]를 눌러서 바로 Custom Class를 오픈해도 됩니다.

Class 항목에서 이전에 만들어 놓은 MyView를 선택합니다.


사용자 삽입 이미지
완료되면 CustomView가 MyView로 변경됩니다. MyView의 사이즈를 좌측과 같이 적절하게 조절 합니다.

상단의 Window에서 SimpleDraw로의 변경은 이전 장에서 해본 것과 같이 윈도우의 인스펙터를 불러내어 Window Title 항목을 변경합니다.






3) 버튼 생성 및 속성 설정

이제 버튼을 배치합니다. 아래와 같이 팔레트에서 버튼을 불러와 버튼을 복사/붙여넣기 하여 네개의 버튼을 만든 후 MyView 하단에 배치 합니다.
사용자 삽입 이미지

사용자 삽입 이미지
좌측과 같이 네개의 버튼의 제목을 각각 사각형, 원, 선, 문자로 변경합니다.

자세히 보면 버튼의 모양이 기본 버튼과 다른데, 이는 아래에서 설명하겠습니다.









사용자 삽입 이미지
사각형 버튼에서 인스펙터를 열어 보면, Tag에 1로 되어 있습니다. 이는 각각의 버튼이 클릭되었을 때, MyView의  DrawItem 메소드에서 구별하기 위해 설정 합니다.

사각형은 1, 원은 2, 선은 3, 문자는 4로 Tag 값을 각각 설정 합니다.

위의 버튼 모양은 Type 항목에서 Round Textured Button을 선택한 모양 입니다.


이제 버튼 클릭시 동작을 연결합니다. 이전 장과 같이 사각형 버튼을 control키를 누른 상태에서 드래그 하여 MyView에 가져다 놓습니다. 아래와 같이 인스펙터 창이 나타나면 Target/Actiond에서 drawItem을 선택하고 하단의 Connect 버튼을 클릭합니다.
사용자 삽입 이미지
위의 작업을 원, 선, 문자 버튼에서도 똑같이 drawItem에 연결 하여 줍니다.


1.5.3 소스코드 작성

1) 헤더파일 수정

이제 Xcode로 돌아 와서 소스코드를 수정합니다. 우선 MyView.h를 에디터에서 오픈합니다.

#import <Cocoa/Cocoa.h>

@interface MyView : NSView
{
    int itemType;
   
    NSString *txt_message;
    NSMutableDictionary *txt_attributes;
}
- (IBAction)drawItem:(id)sender;
@end

헤더파일에는 아래와 같이 세가지 속성들이 추가되었습니다.

int itemType;
어떤 방식으로 그릴지 설정을 저장할 변수 입니다. 위에서 버튼들의 인스펙터에서 설정 한 tag값(1, 2, 3, 4)들이 저장됩니다.

NSString *txt_message;
출력할 문자를 저장합니다.

NSMutableDictionary *txt_attributes;
폰트, 색상 등 문작의 출력 속성을 저장 합니다.

2) 소스 파일 변경 사항

이제 MyView.m을 에디터에서 오픈합니다. 이번에는 소스가 조금 길어 졌습니다.

#import "MyView.h"

@implementation MyView

- (id)initWithFrame:(NSRect)frameRect
{
    if ((self = [super initWithFrame:frameRect]) != nil) {
        // Add initialization code here
        itemType = 0;
        
        txt_message = [[NSString alloc] initWithString:@"Hello!!!"];
        txt_attributes = [[NSMutableDictionary alloc] init];
        [txt_attributes setObject:[NSColor blueColor]
                                   forKey:NSForegroundColorAttributeName];
    }
    return self;
}

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

- (void)drawRect:(NSRect)rect
{
    const int ITEM_WIDTH = 50;
    const int ITEM_HEIGHT = 50;
   
    NSRect itemRect;
    NSBezierPath *path;
        
    itemRect.origin.x = (rect.size.width/2) - (ITEM_WIDTH/2);
    itemRect.origin.y = (rect.size.height/2) - (ITEM_HEIGHT/2);
   
    itemRect.size.width = ITEM_WIDTH;
    itemRect.size.height = ITEM_HEIGHT;

    switch (itemType) {
    case 1:
        path = [NSBezierPath bezierPathWithRect:itemRect];
        [path fill];
        break;
    case 2:
        path = [NSBezierPath bezierPathWithOvalInRect:itemRect];
        [path fill];
        break;
    case 3:
        path = [NSBezierPath bezierPath];
        [path moveToPoint:itemRect.origin];
        [path lineToPoint:(NSPoint) { itemRect.origin.x + itemRect.size.width, itemRect.origin.y + itemRect.size.height }];
        [path setLineCapStyle: NSButtLineCapStyle];
        [path setLineWidth: 3];
        [path stroke];
        break;
    case 4:
        [txt_message drawAtPoint:(NSPoint) { 20, 20 } withAttributes:txt_attributes];
        break;
    default:
        break;
    }
}

- (IBAction)drawItem:(id)sender
{
    NSLog(@"%d", [sender tag]);
   
    itemType = [sender tag];        
    [self setNeedsDisplay:YES];
}

@end

3) 오브젝트 생성과 해제

자동으로 생성된 initWithFrame 메소드는 이름에서 짐작하듯이 NSView가 처음 초기화 될 때 자동으로 호출됩니다. 주석 설명(// Add initialization code here)과 같이 초기화할 내용들을 아래와 같이 입력합니다.

itemType = 0;

초기에 아무것도 그려지지 않도록 0으로 넣습니다.
      
txt_message = [[NSString alloc] initWithString:@"Hello!!!"];
출력할 텍스트 오브젝트를 생성하고 "Hello!!!"란 문구로 초기화 합니다.

txt_attributes = [[NSMutableDictionary alloc] init];
[txt_attributes setObject:[NSColor blueColor]
                            forKey:NSForegroundColorAttributeName];
텍스트 속성 오브젝트를 생성하고, 폰트칼라만 파란색([NSColor blueColor])으로 설정합니다.

- (void)dealloc {
    [txt_message release];
    [txt_attributes release];
    [super dealloc];
}
dealloc은 오브젝트 사용이 끝나고 메모리에서 해제될 때 호출됩니다. alloc으로 메모리를 할당한 오브젝트들(txt_message, txt_attributes)을, release로 메모리를 해제해 줍니다.

4) 그리기

drawRect는 NSView가 그려져야 할 때, 자동으로 호출됩니다. 그리는 작업을 이곳에서 하면 됩니다.

const int ITEM_WIDTH = 50;
const int ITEM_HEIGHT = 50;
그려질 도형들의 크기를 설정합니다.
  

NSRect itemRect;
NSBezierPath *path;
        
itemRect.origin.x = (rect.size.width/2) - (ITEM_WIDTH/2);
itemRect.origin.y = (rect.size.height/2) - (ITEM_HEIGHT/2);
itemRect.size.width = ITEM_WIDTH;
itemRect.size.height = ITEM_HEIGHT;
그려질 도형들의 위치와 크기를 설정합니다. 인자로 넘어온 rect는 그려져야 할 영역인데 NSView의 크기로 보시면 됩니다. NSView의 중앙에 오도록 계산하여 x, y를 설정 합니다.
 

switch (itemType) {
case 1:
    path = [NSBezierPath bezierPathWithRect:itemRect];
    [path fill];
    break;
case 2:
    path = [NSBezierPath bezierPathWithOvalInRect:itemRect];
    [path fill];
    break;
case 3:
    path = [NSBezierPath bezierPath];
    [path moveToPoint:itemRect.origin];
    [path lineToPoint:(NSPoint) { itemRect.origin.x + itemRect.size.width, itemRect.origin.y + itemRect.size.height }];
    [path setLineCapStyle: NSButtLineCapStyle];
    [path setLineWidth: 3];
    [path stroke];
    break;
case 4:
    [txt_message drawAtPoint:(NSPoint) { 20, 20 } withAttributes:txt_attributes];
    break;
default:
    break;
} 
itemType에 따라 NSBezierPath를 사용하여 사각형, 원, 직선과 문자를 출력합니다. 상세한 설명은  튜토리알 이 후에 다시 하겠습니다. 용도는 오브젝트, 변수, 메소드 명을 보시면 대충 짐작하실 수 있습니다.

다만 [txt_message drawAtPoint:(NSPoint) { 20, 20 } withAttributes:txt_attributes];
를 보시면 x, y 20, 20으로 출력하는데 생각과는 달리 좌측 상단에 나오지 않고 좌측 하단에 출력 됩니다. 이는 기본 좌표 체계가 좌측 하단을 기준으로 시작하기 때문입니다.

5) 사용자 입력 처리

- (IBAction)drawItem:(id)sender
{
    NSLog(@"%d", [sender tag]);
   
    itemType = [sender tag];        
    [self setNeedsDisplay:YES];
}
버튼이 클릭될 때, 호출되는 메소드 입니다. 네개의 버튼이 연결되어 잇지만 [sender tag]로 현재 어느 버튼이 클릭되었는지 알 수 있습니다.

[self setNeedsDisplay:YES];
NSView가 다시 그려져야 됨을 알립니다. drawRect가 호출됩니다.

6) 마무리

이제 빌드 후 실행하고 테스트 해 봅니다. 오류가 나거나 정상적으로 동작하지 않으면 위의 내용을 확인해 보시기 바랍니다.

완전한 이해를 위해서는 Object-c의 문법과 메소드 기타 상세한 설명이 필요 하지만, 단순히 따라 해보는 튜토리알에 의미를 두었습니다. 추후 튜토리알과 다른 항목에서 설명하도록 할려고 합니다.

또한 결정적인 이유는 송구스럽게도 저도 대충 이해하고 구현에 중점을 두고 해보기 때문입니다.
 

AND

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

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

1.4.2  AppController class 생성 및 변경
이전 장과 똑같은 방법으로 AppController.h와 AppController.m 파일을 생성합니다.

1) AppController.h 파일 변경

Xcode에서 생성된 AppController.h 파일을 편집창에서 열어 아래와 같이 파란색 라인을 추가합니다.
#import <Cocoa/Cocoa.h>

@interface AppController : NSObject {
    IBOutlet NSTextField *txtResult;
    IBOutlet NSTextField *txtValue;
}

- (IBAction)plusClicked:(id)sender;
- (IBAction)minusClicked:(id)sender;
- (IBAction)muntiplyClicked:(id)sender;
- (IBAction)devideClicked:(id)sender;

@end

IBOutlet NSTextField *txtResult;
IBOutlet NSTextField *txtValue;
위와 같이 계산된 결과값을 표시해 줄 txtResult와 사용자가 입력하는 숫자를 입력 받을 txtValue를 선언 합니다.

- (IBAction)plusClicked:(id)sender;
- (IBAction)minusClicked:(id)sender;
- (IBAction)muntiplyClicked:(id)sender;
- (IBAction)devideClicked:(id)sender;
위와 같이 더하기/빼기/곱하기/나누기 연산을 처리 할 메소드를 선언합니다.

2)  AppController.m 파일 변경

다음은 AppController.m 파일을 편집창에서 열어 아래와 같이 파란색 라인을 추가합니다.
#import "AppController.h"


@implementation AppController
- (IBAction)plusClicked:(id)sender
{
    float curResult = [txtResult floatValue];
    float curValue = [txtValue floatValue];
   
    curResult += curValue;
    [txtResult setFloatValue:curResult];
    [txtValue setStringValue:@""];
}

- (IBAction)minusClicked:(id)sender
{
    float curResult = [txtResult floatValue];
    float curValue = [txtValue floatValue];
   
    curResult -= curValue;
    [txtResult setFloatValue:curResult];
    [txtValue setStringValue:@""];
}

- (IBAction)muntiplyClicked:(id)sender
{
    float curResult = [txtResult floatValue];
    float curValue = [txtValue floatValue];

    if(curValue == 0.0f)
        return;

    curResult *= curValue;
    [txtResult setFloatValue:curResult];
    [txtValue setStringValue:@""];
}

- (IBAction)devideClicked:(id)sender
{
    float curResult = [txtResult floatValue];
    float curValue = [txtValue floatValue];

    if(curValue == 0.0f)
        return;
   
    curResult /= curValue;
    [txtResult setFloatValue:curResult];
    [txtValue setStringValue:@""];
}

@end

나중에 인터페이스 빌더에서 만들어질 +, -, *, / 버튼이 클릭되었을 때, 행동을 지정합니다. 모두 같은 기능을 하니 plucClicked만 예를 들겠습니다.

float curResult = [txtResult floatValue];
결과 창의 현재 값을 가지고 옵니다. 소수점을 지원하기 위해 float으로 선언합니다.

float curValue = [txtValue floatValue];
현재 사용자가 입력한 값을 가지고 옵니다.

curResult += curValue;
사용자가 입력한 값을 현재 값에 더합니다. curResult = curResult + curValue; 과 같은 의미입니다. 더하기(+)와 마이너스(-)는 일반 사용하는 기호와 같지만 곱하기는 "*", 나누기는 "/"를 사용합니다.

[txtResult setFloatValue:curResult];
계산된 값을 텍스트 필드에 저장 합니다. 이 명령으로 사용자는 최종 계산된 값을 볼 수 있습니다.

[txtValue setStringValue:@""];
다음 입력을 위하여 현재 입력창의 내용을 지웁니다. 쌍따움표 앞에 @는 아스키 코드를 NSString 형태로 바꾸어 줍니다. @""로는 한글은 표현할 수 없습니다.
 
곱하기와 나누기에는 아래의 내용이 더 추가되어 있습니다.
if(curValue == 0.0f)
        return;
사용자가 미입력시나 0을 입력하였을 경우, 0으로 곱하거나 나누어지는 것을 방지합니다.

3) Nib에 인스턴스 생성

이제 소스파일에서 변경된 사항을 저장하고 Xcode의 AppController.h를 마우스로 드래그 하여 인터페이스 빌더의 윈도우로 가져다 놓습니다. classes 윈도우에서 AppController를 우클릭 하여, Instantiate AppController를 클릭하여 아래와 같이 인스턴스를 생성합니다.
사용자 삽입 이미지

1.4.3  인터페이스 빌더에서 윈도우 생성 및 연결

1) 컨트롤 추가

윈도우를 틀릭하여 아래와 같이 컨트롤들을 추가하고 배치 합니다.
사용자 삽입 이미지

텍스트 필드 두개와  버튼 네개 (버튼은 하나를 가져 온 후, 복사/붙여 놓기로 사용하셔도 됩니다.)를 가져다 놓습니다. 상하 구분선은 팔레트의 두번째 Controls 항목에 있으며, 아래의 화살표를 참조 하여 마우스로 드래그 해 오시면 됩니다.
사용자 삽입 이미지

텍스트 필드는 숫자 형식으로 보여지기 위해, 아래의 우측에 보이는 팔레트에서 1.99$로 표시되어 있는 숫자형식을 지정하는 콘트롤을 드래그 해서 윈도우의 텍스트 필드에 가져다 놓습니다. 그 하단의 텍스트 필드에도 위와 같이 숫자형식 콘트롤을 드래그 해 놓습니다.
사용자 삽입 이미지

윈도우의 텍스트 필드를 클릭하고 [command + shift + i]를 클릭하여,  인스펙터를  오픈합니다. 숫자를 표현하기 위해 좌측 Attributes 항목에서 Alignment를 세번째 우측 정렬로 선택합니다. 위의 숫자형식 콘트롤을 추가하였기 때문에 오른쪽 이미지와 같이 마지막 속성에 Formmatter란 숫자형식을 지정할 수 있는 속성이 추가되었습니다. 첫번째 항목을 선택 합니다. 위의 작업 역시 하단의 텍스트 필드에도 똑같이 해줍니다.

가장 위의 결과창(모서리가 동그란 입력창)은 보여주기만 하므로 Options에서 Editable 클릭을 해제합니다.
사용자 삽입 이미지 사용자 삽입 이미지

2) AppCrontroller와 연결

이제는 AppConroller의 속성과 메소드를 연결합니다. AppController를 controll키와 함께 드래그하여 상단의 텍스트 필드와 txtResult 아울렛에 연결합니다. 마찬가지로 하단 왼쪽의 텍스트 필드와 txtValue와 연결합니다.

사용자 삽입 이미지

각각의 버튼들 controll키와 함께 AppController로 드래그 하여 + -> plusClicked, - -> minusClicked, * -> multiflyClicked, / -> devideClicked 로 연결(Connect)합니다.
사용자 삽입 이미지

위의 인터페이스 빌더의 일련의 작업들이 이해가 안가시는 분은 이전 포스트를 참조해 주세요.

1.4.4 테스트 및 추가사항
이제 모든 작업이 완료되었습니다. Build And Go를 클릭합니다. 빌드가 완료되면 아래와 같이
윈도우가 나타납니다. 좌측 아래 사각 입력창에 숫자를 입력하고 버튼들을 차례로 누르면서 결과값을 확인해 봅니다.
사용자 삽입 이미지

정말로 간단한 계산기가 완료 되었습니다. 다음번엔 여기에 기능들을 추가하고 조금은 어플리케이션 다운모습으로 만들어 볼려고 합니다.

저도 공부하면서 올리는 중이니 용어 사용이나,  구현에 있어 적절치 못한 부분이 많이 있을 것으로 생각됩니다. 지적과 조언은 언제나 감사하게 받아 들이겠습니다.



AND

1.3.1 소스코드 생성
1) 소스코드 생성

사용자 삽입 이미지
툴바에서 Action버튼을 클릭하여 Add/New File... 을 클릭합니다. 키보드에서 [Command + N]을 누르거나 Xcode메뉴에서 File/New File을 클릭해도 같습니다.



다음 나타나는 메뉴에서 아래와 같이 Cocoa/Objective-C class를 선택 하고, 하단의 Next 버튼을 클릭합니다.
사용자 삽입 이미지

다음 메뉴에서 File Name을 아래와 같이 AppController로 입력하고, Finish 버튼을  클릭합니다. (Object-C에서 헤더는 *.h, 소스파일은 *.m이 확장자 입니다)
사용자 삽입 이미지

Xcode 좌측 메뉴를 보면 아래와 같이 AppController.h, AppController.m 두개의 파일이 생성되어 있습니다. 우측과 같이 두 파일을 드래그해서 Classes 그룹 밑으로 둡니다. 옮기지 않아도 상과 없으나, 성격에 맞는 그룹에 정리해 두는 것이 좋습니다.
사용자 삽입 이미지 사용자 삽입 이미지

2) 소스코드 수정

AppController.h 파일을 열고 아래와 같이 추가 합니다. 좌측 파일 목록 에서 해당 파일을 더블 클릭 하거나, 마우스로 클릭 후, 툴바의 Editor를 클릭 합니다.
/* AppController */

#import <Cocoa/Cocoa.h>

@interface AppController : NSObject
{
    IBOutlet id myText;
}

- (IBAction)changeText:(id)sender;
@end

헤더파일을 보면 Java나 C/C++을 아는 분들은 익숙하면서도 뭔가 생소한 느낌이 들 것 입니다. 아래의 내용은 기존에 다른 개발 언어를 경험해 보신 분들은 참고 하시고, 아니면 그냥 건너  뛰시면 됩니다.
 
1. 주석
우선은 주석이 C, Java와 동일하게 "/*" 로 시갖해서 "*/"로 끝난다는 것을 알 수 있습니다.

2. 헤더파일 인클루드
#import로 필요한 헤더파일을 포함하는데, C와 같이 #를 전처리기로 사용하고 Java와 같이 include 대신에 import를 사용합니다.(C와 다른 점은 중복될 경우에는 오류없이 하나만 포함합니다.)

3. 클래스 선언
클랙스는 @interface로 시작해서 @end로 끝이 납니다. 맴버 변수(속성)만 { }에 포함되고 맴버 함수(메소드)는 {} 밖에서 선언되는 것 같습니다.

4. 접두어
cocoa에서는 내장 클래스들이 NS라는 접두어를 사용하고 있습니다.  NextSTEP의 NS에서 온 것 같다는 생각이 들지만 확실하지는 않습니다.

IB로 시작되는 것은 Interface Builder에서 연결된다는 의미인 것 같습니다. 둘 다 자동화를 위한 Xcoide의 예약어로 생각하시면 될 것 같습니다. (IBAction)은 리턴 값 타입이 오는 자리이지만 IBAction은 void와 같습니다.

아래 두개의 라인이 추가되었습니다.
IBOutlet id myText;
    윈도우의 Hellow World가 있는 텍스트 필드에 연결될 변수 입니다.

- (IBAction)changeText:(id)sender;
    변경 버튼이 클릭되었을 때 연결될 메소드 입니다.

이젠 AppController.m 파일을 오픈 합니다.
#import "AppController.h"

@implementation AppController
- (IBAction)changeText:(id)sender
{
    [myText setStringValue:@"Hello World!];
    NSLog(@"Text changed");
}
@end

AppControll.h에서 선언 해 놓은 ChangeMyText 메소드를 구현 합니다. 이 메소드는 변경 버튼이 클릭되었을 때, 실행됩니다.

[myText setStringValue:@"Hello World!];
버튼이 클릭되면 myText(Hello World 택스트 필드)가 "Hellow World!"로 문자를 변경하라는 의미입니다. Hello World! 다음에 "로 막아야 하지만, 오류 메시지를 보기 위해 입력하지 않았습니다.
사용자 삽입 이미지 사용자 삽입 이미지

NSLog(@"Text changed");
Log윈도우에 메세지를 출력해 주는 함수 입니다. 변수값 확인, 오류를 찾거나 수행여부를 확인할 때 사용할 수 있습니다.

이제 툴바의 Build 버튼([command + b])을 클릭 해서 컴파일을 합니다. 하단을 보면 컴파일 중이라는 메시지 두에 Build failed (2 errors)란 메시지를 볼 수 있습니다. 2개의 오류가 생겨서 컴파일을 완료하지 못했다는 뜻 입니다.
사용자 삽입 이미지

에디터를 보면 위와 같이 X 마트로 오류가 난 위치가 표시되어 있습니다. 상세한 오류 메시지를 보실려면 좌측의 Errors and Warnings를 클릭한 후 파일별로 내용을 확인할 수 있습니다.

또는 메뉴바에서 Build/Build results [shift + command + b]를 클릭하시면, 아래와 같이 컴파일 결과를 확인할 수 있습니다.
사용자 삽입 이미지
에디터에서 다음 오류 라인은 [command + =], 이전 오류 라인은 [command + shift + =]로 오류지점으로 이동할 수 있습니다.

[myText setStringValue:@"Hello World!"];
이제 위와 같이 Hellow World! 뒤에 "를 입력하고 Build and Go [command + r]
를 클릭 합니다. 실행이 되면 이전과 마찬가지로 아직은 어떠한 작동도 하지 않습니다. 이제 버튼 클릭에 동작하는 작업을 해보도록 하겠습니다.


1.3.2 인터페이스 빌더에서 추가 작업
다시 Xcode로 돌아 와 좌측 메뉴의 NIB Files/MainMenu.nib를 더블클릭 하여 인터페이스 빌더를 엽니다.

Xcode에서 AppController.h가 저장되어 있음을 확인합니다. 아래 좌측과 같이 m 아이콘이 어두우면 소스 변경 후 저장이 되지 않은 상태이며, 흰색이면 최종 변경 후 저장되어 있는 상태 입니다.
사용자 삽입 이미지 사용자 삽입 이미지

1) AppController.h 등록  과 인스턴스 생성

아래와 같이 Xcode의 좌측 메뉴에서 AppController.h를 드래그 하여 인터페이스 빌더의 Instances 윈도우로 가져다 놓습니다.
사용자 삽입 이미지

완료되면 아래와 같이 Classes 윈도우에 AppController가 등록되어 있는 것을 확인할 수 있습니다.
사용자 삽입 이미지

사용자 삽입 이미지
위에 보이는 AppController를 마우스 우측 버튼으로 클릭(control + 클릭)하시면, 좌측과 같은 메뉴가 나옵니다. 여기서  Instantiate  AppController를 클릭 합니다.


Instances 윈도우에 AppController 라는 인스턴스가 생성되었습니다.
사용자 삽입 이미지

2) 버튼 등록

이제 윈도우에 버튼을 추가 합니다. 아래 왼쪽 그림과 같이 팔레트의 두번째 버튼 항목에서  버튼을 클릭 후 드래그 해서 오른쪽 그림에서 보이는 윈도우의 적당한 위치에 가져다 놓습니다. 그 후 버튼을 더블클릭하여 Button이라는 문자를 "바꾸기"로 변경합니다.
사용자 삽입 이미지 사용자 삽입 이미지

3) changeText와 버튼 연결

버튼이 추가되면 이제 버튼 클릭시  AppController에서 만들어 놓은 changeText 메소드로 연결 합니다. control 키를 누른 상태에서 마우스 버튼을 바꾸기 버튼에 누른 상태에서 라인이 생기면 AppController의 인스턴스(파란  네모 상자)에다 놓습니다.

그러면 Inspector의 Connections의 Target/Action에서 아래 왼쪽 그림과 같이 changeText 항목이 나타나는데, 이를 선택하면 아래의 connect 버튼이 활성화 된다. connect 버튼을 클릭하면 changeText 앞에 아이콘이 나타나며 연결됩니다.
사용자 삽입 이미지 사용자 삽입 이미지
이제 버튼이 클릭되면 AppController의 changeText 메소드로 연결됩니다.

4) myText와 텍스트필드 연결

텍스트필드와 AppController의 myText를 연결합니다. 다시 control 키를 누른 상태에서 위와는 반대로 AppConroller(파란 네모 상자)를 마우스 버튼을 클릭하여 라인이 생기면 Hello World라고 되어 있는 텍스트 필드에 연결합니다.

연결이 되면 위와 같이 Inspector가 나오는데, Connections의 Outlets에서 왼쪽 그림과 같이 myText 항목이 있다. 위와 같이 myText를 선택하고 connect를  클릭하고, myText 앞에 아이콘이 있어 연결이 되었는지 확인 합니다.
사용자 삽입 이미지 사용자 삽입 이미지
이제 myText와 텍스트 필드가 연결되었습니다. Build And Go 버튼을 눌러 컴파일을 하고 잘 동작하는지 테스트를 해봅니다.

5) 실행 및 확인
컴파일이 오류없이 완료되면 Hello 윈도우가 실행됩니다. 바꾸기 버튼을 클릭하면 "!"가 추가된 텍스트로 오른쪽 아래와 같이 변경됩니다.
사용자 삽입 이미지 사용자 삽입 이미지

Xcode를 보면 버튼 클릭 시, 아래와 같이 NCLog에서 출력하는 "Text changed"란 메세지가 나옵니다.
사용자 삽입 이미지

 
AND