Beginning cocoa programming: delegates

Xcode 3.2.2

Mac OS X 10.6.8

Cocoa Programming for Mac OS X (third edition)

Chapter 6: Helper objects


What happened to the forum software? I can't quote text/code anymore.


I'm trying to create a simple delegate for an NSWindow. The delegate implements this method:


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


I can get my delegate to work if I use Interface Builder to connect my app's main(and only) window to an instance of my object(which appears in the MainMenu.xib inspector in IB). However, I cannot get a delegate to work if I call setDelegate: on my app's main window. In the book, there is one example where they use setDelegate: to setup the delegate object. There is another example where they use IB to hook up the delegate. The only distinction I can detect between the two examples is that in the one that employs setDelegate:, the delegate-or is not a widget, and in the one that uses IB to make the connection, the delegate-or is a widget.


This is the code that I used to try and programmatically set up a delegate:


//AppController.h


#import <Cocoa/Cocoa.h>


@interface AppController : NSObject {


IBOutlet NSWindow* window;

}

- (id)init;



@end




//AppController.m


#import "AppController.h"

#import "MyDelegate.h"


@implementation AppController


- (id)init

{


if (![super init]) {

return nil;

}


MyDelegate* aDelegate = [[MyDelegate alloc] init];

[window setDelegate:aDelegate];


return self;

}



@end




//MyDelegate.h


#import <Cocoa/Cocoa.h>


@interface MyDelegate : NSObject <NSWindowDelegate> {


}

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



@end




//MyDelegate.m


#import "MyDelegate.h"



@implementation MyDelegate



- (NSSize)windowWillResize:(NSWindow *)sender

toSize:(NSSize)frameSize

{

frameSize.height = 2 * frameSize.width;

return frameSize;

}



@end



In IB, I went into the Library, and I dragged an Object to the MainMenu.xib window/inspector, and I named it AppController. Then I dragged a connection from the AppController outlet named 'window' to the NSWindow. When I build&run my code, I can resize the window to any size--the window size is not constrained to a 2:1 ratio. That is not the result I expect: I expect the window size to maintain a 2:1 ration. If I set an object's delegate, then when the event occurs, the object should grab the delegate and use it to call the delegation methods; there is nothing more I should have to do in my code. What am I not understanding about cocoa delegates?

Posted on Apr 5, 2013 2:46 PM

Reply
3 replies

Apr 5, 2013 3:04 PM in response to 7stud

By the way, I know how to get my example to work: I can just delete my AppController class, then go into IB and create a MyDelegate instance, and then drag a connection from the window to the MyDelegate instance. What I want to know is why calling setDelegate: is not sufficient to setup a delegate.

Apr 6, 2013 1:19 AM in response to 7stud

I constructed another simple example of a delegate: I created an app with one lone NSTextField on the window. I wanted to see if setDelegate: would work with a different widget(NSTextField v. NSWindow). My goal was to see if I could setup a delegate using setDelegate: on an NSTextField, such that a delegate method would be called when I typed in the text field. I looked through the documentation, and I decided to to see if implementing this delegate method would work:


- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor;



I created a new app(deleting the classes that Xcode automatically creates, as well as the instance in the MainWindow.xib window in IB), and then I created this class:



//MyDelegate.h


import <Cocoa/Cocoa.h>


@interface MyDelegate : NSObject <NSTextFieldDelegate> {

}

- (BOOL)control:(NSControl *)control

textShouldBeginEditing:(NSText *)fieldEditor;


@end



//MyDelegate.m


@implementation MyDelegate


- (BOOL)control:(NSControl *)control

textShouldBeginEditing:(NSText *)fieldEditor

{

NSLog(@"***In delegate method...");

return YES;

}


@end



1) In IB, I created an instance of MyDelegate by dragging an Object from the Library onto the MainWindow.xib window. I entered the name MyDelegate in the TextField Identity Inspector.


2) In IB, I ctrl-clicked on the NSTextField, and then I dragged a connection from NSTextField's 'delegate' outlet to the MyDelegate instance(in the MainWindow.xib window).


3) Saved everything, build&run, and when I typed in the text field, I saw the logged message in the Debugger Console, as expected.


Next, I attempted to create code that uses setDelegate: to achieve the same thing as above.


4) In IB, I ctrl-clicked on the NSTextField and deleted the connection from the 'delegate' outlet to the MyDelegate instance.


5) I made these changes to the code:


//MyDelegate.h


#import <Cocoa/Cocoa.h>


@interface MyDelegate : NSObject <NSTextFieldDelegate> {


IBOutlet NSTextField* textField;

}

- (id)init;

- (BOOL)control:(NSControl *)control

textShouldBeginEditing:(NSText *)fieldEditor;


@end



//MyDelegate.m

#import "MyDelegate.h"


@implementation MyDelegate


- (id) init

{

if (![super init]) {

return nil;

}


[textField setDelegate:self];


return self;

}


- (BOOL)control:(NSControl *)control

textShouldBeginEditing:(NSText *)fieldEditor

{

NSLog(@"***In delegate method...");

return YES;

}


@end



6) In IB, I ctrl-clicked on the MyDelegate instance in the MainWindow.xib window, and then I dragged a connection from the outlet 'textField' to the NSTextField(in the app's main window).


7) I saved in IB, then build&run (save everything), and this time when I typed in the textfield, the message was not logged to the Debug Console.


What is the matter with setDelegate:? Why doesn't setDelegate: succeed in setting up the delegate so that I can see the message logged to the console?


Thanks

Apr 6, 2013 3:03 AM in response to 7stud

I think I found the answer to what's happening with setDelegate:, which by the way no one on the internet seems to be aware of, including the author of the book. At this link:


http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Applicatio nKit/Protocols/NSNibAwaking_Protocol/Reference/Reference.html%23//apple_ref/occ/ instm/NSObject/awakeFromNib


...it says:


===

An

awakeFromNib
message is sent to each object loaded from the archive, but only if it can respond to the message, and only after all the objects in the archive have been loaded and initialized. When an object receives an
awakeFromNib
message, it is guaranteed to have all its outlet instance variables set.

===


Huh?!! You mean it's NOT guaranteed that the objects pointed to by a class's instance variables exist??? Let's read on:


===

During the instantiation process, each object in the archive is unarchived and then initialized with the method befitting its type....


Once all objects have been instantiated and initialized from the archive, the nib loading code attempts to reestablish the connections between each object’s outlets and the corresponding target objects. If your custom objects have outlets, an

NSNib
object attempts to reestablish any connections you created in Interface Builder...


Finally, after all the objects are fully initialized, each receives an

awakeFromNib
message.

===


So that means that inside an init() method, you can't send messages to (i.e call methods on) other objects--including those objects that are pointed to by your class's instance variables. During the initialization phase, i.e. in your init() methods, you have to assume that no other objects exist (unless you specifically created those objects inside your init() method). As a result, if you want to call methods on other objects, you have to do it in awakeFromNib at such time when all the other objects in your app are guaranteed to exist.


I modified my code to call setDelegate: in my class's awakeFromNib method. Now the code works as expected and I am able to see the delegate method log its message in the Debug Console:


//MyDelegate.h


#import <Cocoa/Cocoa.h>


@interface MyDelegate : NSObject <NSTextFieldDelegate> {


IBOutlet NSTextField* textField;

}

- (id)init;

- (void)awakeFromNib;

- (BOOL)control:(NSControl *)control

textShouldBeginEditing:(NSText *)fieldEditor;


@end




//MyDelegate.m


#import "MyDelegate.h"


@implementation MyDelegate


- (id) init

{

if (![super init]) {

return nil;

}


//[textField setDelegate:self];


return self;

}



- (void)awakeFromNib

{

[textField setDelegate:self];

}


- (BOOL)control:(NSControl *)control

textShouldBeginEditing:(NSText *)fieldEditor

{

NSLog(@"***In delegate method...");

return YES;

}


@end



Hmmm...I think a good author/teacher would have mentioned that slightly important fact.

This thread has been closed by the system or the community team. You may vote for any posts you find helpful, or search the Community for additional answers.

Beginning cocoa programming: delegates

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple Account.