본문 바로가기
조회 수 7568 추천 수 0 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

+ - Up Down Comment Print Files
?

단축키

Prev이전 문서

Next다음 문서

+ - Up Down Comment Print Files
Chapter 6 Helper Objects

참고: 델리게이트 및 프로토콜에 대한 참조 문서

기존의 Cocoa 클래스를 확장하기 위해서 종종 helper 객체를 생성한다. 여기서는 helper 객체를 생성하고 그것을 표준 Cocoa 객체에 연결하는 것에 대해서 다룬다.

** Delegates

이전에 작성한 SpeakLine 애플리케이션을 보면 Speak 버튼을 누르고 음성이 나오는 상태에서도 Speak 버튼을 누르는 것이 가능했다. 여기서 음성이 나오는 동안 Speak 버튼을 비활성화 시키도록 한다.

1. cocoa framework의 많은 클래스들이 delegate 라 불리는 인스턴스 변수를 가지고 있다. delegate outlet을 helper object로 포인팅할 수 있다. 클래스 문서를 보면 delegate 메소드가 설명되어 있다. 예를 들면, NSSpeechSynthesizer 클래스는 아래와 같은 delegate 메소드를 가지고 있다.

- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)finishedSpeaking;
- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender willSpeakWord:(NSRange)characterRange ofString:(NSString *)string;
- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender willSpeakPhoneme:(short)phonemeOpcode;

----------- (참고) 첫번째 메소드 설명 ---------------
메소드타입 - 인스턴스 메소드
메소드이름 - speechSynthesizer:didFinishSpeaking:
반환타입 - void

첫번째 인자 이름 - 없음
첫번째 인자 타입 - NSSpeechSynthesizer
첫번째 인자 변수이름 - sender

두번째 인자 이름 - didFinishSpeaking
두번째 인자 타입 - BOOL
두번째 인자 변수이름 - finishedSpeaking
------------------ end -----------------------------

SpeakLine 애플리케이션에서 SpeakLineAppDelegate에 위 delegate를 생성하고 speechSynthesizer:didFinishSpeaking: 을 수행하도록 한다.

object diagram은 아래와 같다.
01.png

SpeechLineAppDelegate.h에서 SpeechLineAppDelegate 클래스 선언을 아래와 같이 변경 (NSSpeechSynthesizerDelegate 추가)하여 컴파일러에게 SpeakLineAppDelegate가 NSSpeechSynthesizerDelegate 프로토콜을 확인한다고 알려준다. 참고로 프로토콜은 10장에서 다룬다.

@interface SpeakLineAppDelegate : NSObject <NSApplicationDelegate, NSSpeechSynthesizerDelegate> {

(참고)
NSSpeechSynthesizerDelegate과 같은 클래스 선언문에서 델리게이트 선언은 생략해도 컴파일 및 실행에 문제가 없었으나, 구현코드(델리게이트 메소드)를 작성시 자동완성이 되지 않아 오타로 인한 오류가능성이 많았음

2. SpeakLineAppDelegate.m에서 delegate outlet을 작성한다.

- (id)init
{
    self = [super init];
    if (self) {
        // 디버깅용 NSLog
        NSLog(@"init");
        
        // 새로운 NSSpeechSynthesizer 인스턴스를 기본 voice 인수와 가지고 생성
        // 참고로 이 인스턴스 참조변수의 선언은 .h 파일에 되어있음
        speechSynth = [[NSSpeechSynthesizer alloc] initWithVoice:nil];
        
        // 델리게이트 설정. 사용자 인터페이스 객체의 경우 이렇게 다로 설정하지 않고 IB의 
        // Assistance Editor를 사용 연결패널의 SpeakLineAppDelegate로 control-drag하면 된다.
        [speechSynth setDelegate:self];
    }
    return self;
}



3. 다음으로 아래의 delegate 메소드를 추가. 여기서는 메시지만 남도록 한다.

// 발성이 끝나거나 Stop 버튼을 누르면 delegate 메소드가 호출된다
- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)finishedSpeaking
{
    NSLog(@"finishedSpeaking = %d", finishedSpeaking);
}



빌드 후 실행한다. 발성이 끝나거나 Stop 버튼을 누르면 delegate 메소드가 호출된다. (발성이 정상적으로 마쳤을 때만 finished-Speaking가 YES가 된다)
02.png

Stop, Speak 버튼을 enable/disable 하기 위해서는 이 버튼들을 위한 outlet이 필요하다. Assistant Editor를 이용해 이 버튼들을 연결하는 startButton, stopButton outlet을 생성한다.
03.png

.h 파일에 아래와 같이 추가됨
------ SpeakLineAppDelegate.h ------
@property (weak) IBOutlet NSButton *stopButton;
@property (weak) IBOutlet NSButton *speakButton;


4. 최초 화면에서 Stop 버튼은 disable 상태로 있어야 하므로 Attributes인 Inspector 에서 버튼을 disable 한다.
04.png

5. SpeakLineAppDelegate.m 에서 Speak 버튼을 누르면 호출되는 SayIt: 메소드에서 해당 버튼을 enable/disable 하도록 추가한다.

- (IBAction)SayIt:(id)sender {
    NSString *string = [textField stringValue];
    
    // 문자열길이가 0인지 체크. 즉, 문자열이 있는지 체크
    if ([string length] == 0) {
        NSLog(@"string from %@ is of zero-length", textField);
        return;
    }
    [speechSynth startSpeakingString:string];
    NSLog(@"Have started to say: %@", string);
    
    [stopButton setEnabled:YES]; // 메소드가 호출되면 stop 버튼은 enable
    [speakButton setEnabled:NO]; // 메소드가 호출되면 speak 버튼은 disable
}



발성이 끝나면 Stop 버튼을 비활성, Speak 버튼을 활성화 시키기 위해 아래 델리게이트 메소드에도 코드를 추가

- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)finishedSpeaking
{
    NSLog(@"finishedSpeaking = %d", finishedSpeaking);
    [stopButton setEnabled:NO]; // 호출되면, 즉 stop 버튼이 눌러지거나 발성이 끝나면 stop 버튼 비활성
    [speakButton setEnabled:YES]; // 호출되면, 즉 stop 버튼이 눌러지거나 발성이 끝나면 speak 버튼 활성
}



6. 빌드 및 실행
05.png 06.png


** NSTableView 및 dataSource

여기서는 사용자가 음성을 변경하도록 만든다.
07.png

위 table view는 데이터의 컬럼들을 보여준다. NSTableView는 dataSource라 불리는 helper 객체를 가지고 있다.
The table view expects its data source to have some methods. We say, “The data source must conform to the NSTableDataSource informal protocol.” This is a fancy way of saying that it must implement these two methods:

선언문에서 아래와 같이 NSTableViewDataSource를 추가한다.

@interface SpeakLine_AppDelegate : NSObject <NSApplicationDelegate, NSSpeechSynthesizerDelegate, NSTableViewDataSource>

-------- note ----------
dataSource 델리게이트 설정시 아래의 두 메소드를 필수적으로 구현해야 한다.
08.png

- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView;
=> dataSource는 출력될 열의 개수를 리턴한다.

- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex;
=> dataSource는 aTableColumn에 있는 rowIndex 열에 출력될 객체를 가지고 리턴한다.
-------- end of note ----------

추가로 테이블의 셀을 변경하려면 아래의 같은 메소드를 사용할 수 있다.

- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex;

Note that you are taking a very passive position in getting data to appear. Your data source will wait until the table view asks for the data. When they first work with NSTableView (or NSBrowser or NSOutlineView, which work in a very similar manner), most programmers want to boss the table view around and tell it, “You will display 7 in the third row in the fifth column.” It doesn’t work that way. When it is ready to display the third row and the fifth column, the table view will ask its dataSource for the object to display. Your class is its servant.

How, then, will you get the table view to fetch updated information? You will tell the table view to reloadData. It will then reload all the cells that the user can see.



1. 다이어그램

여기서는 SpeakLineAppDelegate 인스턴스가 table view의 dataSource가 된다. 이를 위해 위에서 언급된 두 메소드를 구현하고 table view의 dataSource outlet를 SpeakLineAppDelegate의 인스턴스로 설정한다.
09.png

2. SpeakLineAppDelegate.h에 새로운 인스턴스 변수를 선언 후 .m에서 초기화

@interface SpeakLine_AppDelegate : NSObject <NSApplicationDelegate, NSSpeechSynthesizerDelegate>
{
    NSArray *voices; // NSArray 인스턴스 변수 추가. 사용가능한 voice들을 저장함
    NSSpeechSynthesizer *speechSynth;
}



SpeakLineAppDelegate.m의 init 메소드에서 voices 초기화

- (id)init
{
    self = [super init];
    if (self) {
        NSLog(@"init");
        speechSynth = [[NSSpeechSynthesizer alloc] initWithVoice:nil];        
        [speechSynth setDelegate:self];
        
        // 인스턴스 변수 voices 초기화
        voices = [NSSpeechSynthesizer availableVoices];
    }
    return self;
}



3. 사용자 인터페이스 생성
10.png

- Content type은 cell, 하나의 column, 그리고 column selection을 disable한다.
- table column의 길이를 끝까지 늘임
- column의 header를 더블클릭하여 Voices로 변경
- table view는 아래와 같이 scroll view 안에 있으며, table view column은 table view 안에 위치한다.
11.png

4. table view를 위한 3개의 연결 생성

<dataSource outlet 연결: table view의 데이터를 위한 dataSource>
- 여기서는 NSTableView의 dataSource outlet을 SpeakLineAppDelegate가 되도록 설정한다.
- table view에서 control-click후 dataSource에서 연결패널로 ctrl-drag한다.
12.png

또는 아래와 같이 table view에서 SpeakLineAppDelegate로 바로 ctrl-drag한다. (권장)
13.png

<table view의 delegate 연결: 선택시 델리게이트 메소트 호출을 위한>
다음으로 SpeakLineAppDelegate가 table view의 delegate가 되도록 설정한다. 
13-2.png

<table view의 outlet 연결: 비활성화 등등에 사용>
Assistant Editor을 사용해 SpeakLineAppDelegate에 tableView라 불리는 outlet을 생성하고 table view에 연결한다.
14.png

5. table view에 있는 row 수를 출력

<SpeakLineAppDelegate.h에서 델리게이션 NSTableViewDataSource 선언 - 델리게이트 메소드가 자동완성됨>
@interface SpeakLine_AppDelegate : NSObject <NSApplicationDelegate, NSSpeechSynthesizerDelegate, NSTableViewDataSource>



<SpeakLineAppDelegate.m에서 아래의 두 data source 메소드를 구현>
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tv
{
    // NSLog에서 NSInteger를 나타내는 specifier로 %ld 사용
    NSLog(@"number of rows in table = %ld", [voices count]);
    return (NSInteger)[voices count];
}

- (id)tableView:(NSTableView *)tv
        objectValueForTableColumn:(NSTableColumn *)tableColumn
                              row:(NSInteger)row
{
    NSString *v = [voices objectAtIndex:row];
    return v;
}



tv 테이블뷰의 tableColumn의 row열에 출력될 객체를 반환
15.png

6. voice를 짧은 이름으로 만들기

빌드 및 실행을 하면 위와 같이 voice의 종류로 com.apple.speech.synthesis.voice.Fred 같은 긴 문자열이 나온다. Fred같은 이름만을 원할 경우 아래와 같이 변경한다.
- (id)tableView:(NSTableView *)tv
        objectValueForTableColumn:(NSTableColumn *)tableColumn
                              row:(NSInteger)row
{
    NSString *v = [voices objectAtIndex:row];
    NSDictionary *dict = [NSSpeechSynthesizer attributesForVoice:v]; // 추가됨
    return [dict objectForKey:NSVoiceName]; // 추가됨
}

16.png

7. voice 변경하기

위에서 voice 리스트가 나오지만 선택을 해도 아무런 동작도 하지 않는다. 위에서 설정한 delegate outlet이 있다.
이 delegate는 테이블뷰에서 선택이 이루어질 때마다 정보가 전달된다. SpeakLineAppDelegate.m 에서 아래와 같이 델리게이션 메소드 tableViewSelectionDidChange: 을 구현하는 코드를 작성한다. (NSNotification 클래스는 나중에 설명한다. 여기서는 notification 객체가 delegate 메소드로 인수로서 전달된다는 것만 알아둔다)

.h 파일에서 NSTableViewDelegate 추가 (자동완성됨)

@interface SpeakLine_AppDelegate : NSObject <NSApplicationDelegate, NSSpeechSynthesizerDelegate, NSTableViewDataSource, NSTableViewDelegate>

.m 파일에서 아래 코드를 추가하여 선택이 이루어질 때마다 실행되도록한다.

- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
    NSInteger row = [tableView selectedRow];
    if (row == -1) {
        return;
    }
    NSString *selectedVoice = [voices objectAtIndex:row];
    [speechSynth setVoice:selectedVoice];
    NSLog(@"new voice = %@", selectedVoice);
}



8. tableView 활성/비활성

speech synthesizer는 발성중에 변경을 허용하지 않는다. 때문에 사용자가 테이블뷰에서 선택하는 것을 막아야 한다. 아래와 같이 speakButton과 함께 테이블뷰도 enable/disable하는 코드를 추가한다.

- (IBAction)SayIt:(id)sender {
    NSString *string = [textField stringValue];
    if ([string length] == 0) {
        NSLog(@"string from %@ is of zero-length", textField);
        return;
    }
    [speechSynth startSpeakingString:string];
    NSLog(@"Have started to say: %@", string);
    
    [stopButton setEnabled:YES];
    [speakButton setEnabled:NO];
    [tableView setEnabled:NO]; // 메소드가 호출되면 테이블뷰 disable
}

- (IBAction)StopIt:(id)sender {
    NSLog(@"stopping");
    [speechSynth stopSpeaking];
    [tableView setEnabled:YES]; // Stop 버튼을 누르면 테이블뷰 enable
}

- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)finishedSpeaking
{
    NSLog(@"finishedSpeaking = %d", finishedSpeaking);
    [stopButton setEnabled:NO];
    [speakButton setEnabled:YES];
    [tableView setEnabled:YES]; // 발성이 끝나면 테이블뷰 enable
}



9. 기본 voice 선택

애플리케이션 시작시 기본 voice가 테이블뷰에 선택되도록 하기위해 awakeFromNib 메소드를 생성한다.

- (void) awakeFromNib
{
    // When the table view appears, the default voice should be selected
    NSString *defaultVoice = [NSSpeechSynthesizer defaultVoice];
    NSInteger defaultRow = [voices indexOfObject:defaultVoice];
    NSIndexSet *indices = [NSIndexSet indexSetWithIndex:defaultRow];
    [tableView selectRowIndexes:indices byExtendingSelection:NO];
    [tableView scrollRowToVisible:defaultRow];
}




** 델리게이트는 어떻게 작동하는가?

델리게이트는 모든 메소드에 대해 수행코드를 작성할 필요는 없다. 하지만 객체가 델리게이트 메소드를 수행할때 해당 메소드가 호출된다. 다른 언어에서는 이런 일들이 불가능하지만 objective c에서는 가능하다? 어떻게?

NSObject는 아래와 같은 메소드가 있다.

- (BOOL)respondsToSelector:(SEL)aSelector

모든 객체는 NSObject를 상속하므로 위의 메소드를 가지게 된다. 위 메소드는 객체가 aSelector라는 메소드를 가지면 YES를 리턴한다. aSelector는 SEL이며, NSString이 아니다.

Imagine for a moment that you are the engineer who has to write NSTableView. You are writing the code that will change the selection from one row to another. You think to yourself, “I should check with the delegate.” To do so, you add a snippet of code that looks like this:

// About to change to row "rowIndex"

// Set the default behavior 
BOOL ok = YES;

// Check whether the delegate implements the method 
if ([delegate respondsToSelector:@selector(tableView:shouldSelectRow:)])
{
	// Execute the method
	ok = [delegate tableView:self shouldSelectRow:rowIndex]; 
}

// Use the return value 
if (ok)
{
	...actually change the selection... 
}



Note that the delegate is sent the message only if it has implemented the method. If the delegate doesn’t implement the message, the default behavior happens. (In reality, the result from respondsToSelector: is usually cached by the object with the delegate outlet. This makes performance considerably faster than would be implied by the code.)

After writing this method, you would carefully make note of its existence in the documentation for your class.

If you wanted to see the checks for the existence of the delegate methods, you could override respondsToSelector: in your delegate object:

- (BOOL)respondsToSelector:(SEL)aSelector
{
	NSString *methodName = NSStringFromSelector(aSelector); 
	NSLog(@"respondsToSelector:%@", methodName);
	return [super respondsToSelector:aSelector];
}


You might want try adding this method to AppController.m now.


** 도전: 델리게이트 만들기

############
# 기본정보
############

새로운 app 생성하고, 윈도우의 델리게이트를 만든다. 윈도우 사이즈로 항상 넓이가 높이의 두배가 되도록 설정한다. 이행할 델리게이트의 시그니처는 아래와 같다.

- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize;

첫번째 인수는 윈도우, 두번째는 사용자가 요청한 사이트를 포함한 C struct 이다.

typedef struct _NSSize { 
float width;
float height; 
} NSSize;

아래는 넓이 200, 높이 100으로 NSSize를 어떻게 생성하는지 보여준다. 

NSSize mySize = NSMakeSize(200.0, 100.0);
NSLog(@"mySize is %f wide and %f tall", mySize.width, mySize.height);

인터페이스 빌더의 Size Inspector에서 최초의 윈도우 사이즈로 설정한다.

############
# 작성
############

1. 프로젝트 생성 및 윈도우 생성

2. Size Inspector에서 사이즈 지정 (200,100)
20.png

3. 클래스 선언에서 아래와 같이 NSWindowDelegate 추가
#import <Cocoa/Cocoa.h>
@interface WindowAppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>


참고: NSWindowDelegate 안해도 app 동작은 하였으나 아래 델리게이트 메소드에서 자동완료가 안됨

4. IB에서 윈도우객체를 App Controller App Delegate에 추가
21.png

5. .m 파일에 델리게이트 메소드 windowWillResize:toSize: 추가
- (NSSize)windowWillResize:(NSWindow *)sender
                    toSize:(NSSize)frameSize
{
    NSSize mySize; // C Struct
    
    // 인수인 frameSize에서 높이를 구한 후 넓이를 계산하여 NSSize 리턴
    mySize.height = frameSize.height;
    mySize.width = mySize.height * 2;
    
    NSLog(@"mySize is %f wide and %f tall", mySize.width, mySize.height);
    
    // 리턴값이 윈도우 사이즈로 반영됨
    return mySize;
}


6. 빌드 및 실행

<참고>
------ 전체 .h 파일 ---------
#import <Cocoa/Cocoa.h>

@interface AppControllerAppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>

@property (assign) IBOutlet NSWindow *window;

@end



------ 전체 .m 파일 ---------
#import "AppControllerAppDelegate.h"

@implementation AppControllerAppDelegate

// app 실행되면 아래의 init 메소드가 실행됨
- (id)init
{
    self = [super init];
    if (self) {
        // 디버깅용 NSLog
        NSLog(@"init");
    }
    return self;
}

// 델리게이트 메소드 windowWillResize:toSize: (윈도우 리사이즈시 호출됨)
- (NSSize)windowWillResize:(NSWindow *)sender
                    toSize:(NSSize)frameSize
{
    NSSize mySize; // C Struct
    
    // 인수인 frameSize에서 높이를 구한 후 넓이를 계산하여 NSSize 리턴
    mySize.height = frameSize.height;
    mySize.width = mySize.height * 2;
    
    NSLog(@"mySize is %f wide and %f tall", mySize.width, mySize.height);
    
    // 리턴값이 윈도우 사이즈로 반영됨
    return mySize;
}

@end



** 도전: Data Source 만들기

############
# 기본정보
############

이제 to-do 리스트 app을 만들기로 한다. 사용자는 text field에 타이핑 후 Add 버튼을 클릭하면 입력한 문자열이 mutable array로 추가되고 리스트의 마지막에 출력된다.

22.png

새로운 문자열이 추가될 때 reloadData 메시지를 테이블로 보낸다.

가능하면 table view를 수정할 수 있게 해본다. (힌트: NSMutableArray는 replaceObjectAtIndex:withObject: 메소드가 있다)

############
# 작성
############

1. 사용자 인터페이스 객체 생성 (text field, add 버튼 및 talbe view 생성)

- Text Field
- Gradient Button: 이름을 Add로 설정
- Table View: Column을 1로하고 cell 길이를 table 끝까지 확장
23.png

아래와 같이 완성됨
24.png

2. text field, button outlet 및 action 설정

- text field의 textField라는 outlet을 생성 (newItemField는 cocoa에서 이미 사용중임)
- button의 addString이라는 action 생성

25.png

26.png

.h에 아래의 코드 추가됨
@property (weak) IBOutlet NSTextField *textField;
- (IBAction)addString:(id)sender;


.m에 아래의 코드 추가됨
@synthesize textField;
- (IBAction)addString:(id)sender {
}

아래와 같이 addString 메소드를 구현하여 버튼을 눌렀을 때 text field의 값을 가져오는지 확인
- (IBAction)addString:(id)sender {
    NSString *item = [textField stringValue];
    NSLog(@"string in text field is %@", item);
}

27.png

3. Array 생성 및 item 추가하기

.h에 NSMutableArray 객체 선언
@interface AppControllerAppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>
{
    NSMutableArray *items;
}


.m에서 init 메소드에 위 인스턴스변수의 초기화 코드 추가
- (id)init
{
    self = [super init];
    if (self) {
        // 디버깅용 NSLog
        NSLog(@"init");
        // .h에서 선언된 NSMutableArray 인스턴스 변수를 초기화
        items = [[NSMutableArray alloc] init];
}


   
.m에서 버튼이 눌러졌을 때 text field의 문자열이 items에 추가되도록 작성
- (IBAction)addString:(id)sender {
    NSString *item = [textField stringValue];
    NSLog(@"string in text field is %@", item);
    
    // text field의 item이 추가됨
    [items addObject:item];
    
    // items 배열의 요소개수를 체크
    NSUInteger arrayCount = [items count];
    NSLog(@"current count of array = %lu", arrayCount);
}

28.png

<참고: NSInteger에 대해>
int와 NSInteger는 둘 다 객체(Object)가 아니다. 이 말은 동적 메모리 할당에 대해 걱정할 필요가 없다는 뜻이다. 이미리 정의된 사이즈가 정해져 있기 때문이다. 이 두 개의 데이터 타입은 스택(stack)에 생성된다. 힙(heap)에 생성되는 것이 아니라는 점에 주의하자. 그러므로 이 두 타입으로 선언된 변수의 값에 접근하기 위해서 포인터(pointer)가 필요 없다. 이와 유사한 데이터 타입으로 int, NSInteger, CGFloat, CGPoint, CGRect(Foundation Data Types Reference를 참고하라.) 등이 있다. 다시 한 번 말하지만 객체가 아니다. 그러므로 포인터(*)가 필요 없다.

4. table view의 dataSource outlet 설정
29.png

5. table view의 outlet 설정
30.png

6. Step4에서 설정한 delegate에서 array의 정보를 table에서 사용하도록 delegate 메소드 구현
먼저 클래스 선언에서 NSTableViewDataSource 추가하여 numberOfRowsInTableView: 메소드 구현시 자동 완성되도록 함

@interface AppControllerAppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate, NSTableViewDataSource>



위의 delegate를 추가하면 실행하면 아래와 같은 에러가 나옴
31.png

아래와 같이 numberOfRowsInTableView: 와 tableView:objectValueForTableColumn:row: 구현부 추가
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
    return (NSInteger)[items count];
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
    NSString *v = [items objectAtIndex:row];
    return v;
}



(참고)
위 tableView:objectValueForTableColumn:row: 메소드는 tableView outlet과 관련이 있는 것이 아님. tableView:objectValueForTableColumn:row:는 NSTableViewDataSource 델리게이트의 메소드이고, tableView outlet은 sourceData로 저장된 데이터르 reload하는데 사용 (button 메소드에서)

7. 버튼을 누를 때 reloadData 메시지를 테이블로 보내 데이터출력을 refresh
- (IBAction)addString:(id)sender {
    NSString *item = [textField stringValue];
    NSLog(@"string in text field is %@", item);
    
    [items addObject:item];
    
    NSUInteger arrayCount = [items count];
    NSLog(@"current count of array = %lu", arrayCount);

	// reloadData 호출하여 table view의 내용을 reload
    [tableView reloadData];
}


8. 빌드 및 실행
32.png

List of Articles
번호 제목 글쓴이 날짜 조회 수
21 Cocoa Programming 정리 08 - NSArrayController file Hojung 2013.02.25 5992
20 Cocoa Programming 정리 07 - Key-Value Coding 과 Key-Value Observing file Hojung 2013.02.20 5171
» Cocoa Programming 정리 06 - 헬퍼 객체 file Hojung 2013.02.19 7568
18 Cocoa Programming 정리 05 - 타겟(Target)과 액션(Action) file Hojung 2013.02.14 6475
17 Cocoa Programming 정리 04 - 메모리 관리 file Hojung 2013.02.13 6372
16 Cocoa Programming 정리 03 - Objective-C file Hojung 2013.02.08 6136
15 Cocoa Programming 정리 02 - 시작하기 file Hojung 2013.02.05 8681
14 Cocoa Programming 정리 01 - Cocoa란? Hojung 2013.02.05 4866
13 객체 변수 선언시 @private 사용 Hojung 2012.09.18 4361
12 테스트 코드 작성 Hojung 2012.08.22 4104
11 NSString 객체의 생성과 변환 Hojung 2012.08.22 5166
10 클래스 프로퍼티 설정 Hojung 2012.08.22 4148
9 클래스와 객체 - 1 Hojung 2012.08.22 4236
8 Interface and Implementation Hojung 2012.08.22 3910
7 Extends vs Implements의 개념과 차이점 - 2 Hojung 2012.08.22 4319
6 Extends vs Implements의 개념과 차이점 - 1 Hojung 2012.08.22 4947
5 포인터 Hojung 2012.08.22 4214
4 조건문 및 순환문 Hojung 2012.08.22 4374
3 변수형 및 객체형 Hojung 2012.08.22 4360
2 hello world Hojung 2012.08.22 4182
Board Pagination ‹ Prev 1 2 Next ›
/ 2

Designed by sketchbooks.co.kr / sketchbook5 board skin

나눔글꼴 설치 안내


이 PC에는 나눔글꼴이 설치되어 있지 않습니다.

이 사이트를 나눔글꼴로 보기 위해서는
나눔글꼴을 설치해야 합니다.

설치 취소

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5