NSWindowController windowDidLoad not called - cocoa

I have a simple Cocoa app using a NSWindowController subclass. In the nib I have set:
File Owner's class to my NSWindowController subclass
The 'Window' outlet of the File's Owner to the main NSWindow in the nib.
The init method of my NSWindowController subclass is called (I call super), but not matter what I do windowDidLoad is never called.
I must be missing something obvious, but for the life of me I can't figure out what it is.

You're trying to create the instance of NSWindowController by instantiating it in another nib. However, when you instantiate an object in a nib file, it is initialized by calling -initWithCoder:.
-initWithCoder: is not a designated initializer of NSWindowController, so your instance of NSWindowController never actually loads its nib.
Instead of instantiating your NSWindowController instance by placing it in the MainMenu.xib file in Interface Builder, create it programmatically:
In AppDelegate.h:
#class YourWindowController;
#interface AppDelegate : NSObject
{
YourWindowController* winController;
}
#end
In AppDelegate.m:
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification*)notification
{
winController = [[YourWindowController alloc] init];
[winController showWindow:self];
}
- (void)dealloc
{
[winController release];
[super dealloc];
}
#end
In YourWindowController.m:
#implementation YourWindowController
- (id)init
{
self=[super initWithWindowNibName:#"YourWindowNibName"];
if(self)
{
//perform any initializations
}
return self;
}
#end

It's perfectly fine to instantiate the window controller through a nib. Rather than use windowDidLoad as your hook, in that case you'll want to use awakeFromNib.

The window might be loaded on demand - try sending window to yourself in -init. See the discussion of -[NSWindowController loadWindow] in the documentation for more info.

if you wrote
TTEst *test3 = (TTEst *)[[NSWindowController alloc] initWithWindowNibName:#"TTEst"];
try instead
TTEst *test3 = [[TTEst alloc] initWithWindowNibName:#"TTEst"];
it makes the difference ! Of course the first line was a mistake...

Related

Incorrect Delegate for Controls on Main Application Window

I am developing an application in XCode 4.6.
To get text-change notifications from NSTextField controls I:
Put NSTextField control on window.
Connect control delegate to File's Owner via right-click in IB, drag from delegate to File's Owner.
Implement controlTextDidChange in window class.
For the application, the window class is my AppDelegate and File's Owner is NSApplication. For the modal dialog, the window class an NSWindowController and File's Owner is of the same type.
If I put a breakpoint in controlTextDidChange, in the AppDelegate class, it never fires. If I do the same procedure with a modal dialog it works fine.
I know in the main application window case the delegate for the control is not my AppDelegate.
What am I doing wrong in hooking up my control delegate in the main window? I must be missing something simple. Is File's Owner the correct delegate to set for controls?
Any help would be appreciated.
Here is some code as requested.
// AppDelegate.h
// SimpleApplication
#import <Cocoa/Cocoa.h>
#import "SimpleTest/SimpleTest.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (assign) IBOutlet NSTextField *textField;
#end
// AppDelegate.m
// SimpleApplication
#import "AppDelegate.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Not much to do here for now.
}
// Breakpoint set in this function never fires.
- (void)controlTextDidChange:(NSNotification *)obj
{
NSMutableString* description= [[NSMutableString alloc] init];
id aDelegate= [_textField delegate];
Class delegateClass= [aDelegate class];
[description setString:[delegateClass description]];
[description release];
}
// To provide some information about the delegates.
- (IBAction)textChange:(id)sender
{
NSTextField* theTextField= (NSTextField*)sender;
NSMutableString* description= [[NSMutableString alloc] init];
id aDelegate= [theTextField delegate];
Class delegateClass= [aDelegate class];
[description setString:[delegateClass description]];
[description release];
}
#end
Here is a shot of the right-click information for the NSTextField on the main window -
Identity inspector shows File's Owner as NSApplication, which is what I see in the debugger when I put a breakpoint in textChange and hit return in the text field. However, self, the implementor of controlTextDidChange, is AppDelegate. By contrast, in a modal dialog, self and File's Owner are the same object, derived from NSWindowController.
So, the upshot is that I do not have the correct delegate assigned to the control in the main window - how do I do that?
Can you post some code?
When using delegates make sure you specify that a class implements the required protocol.
#interface MyClass : NSObject <SomeProtocol>
Also make sure you are creating a property to store the delegate.
#property (strong, nonatomic) id<SomeProtocol> delegate;
RE this:
Note that although NSControl defines delegate methods, it does not
itself have a delegate. Any subclass that uses these methods must have
a delegate and the methods to get and set it. In addition, a formal
delegate protocol NSControlTextEditingDelegate Protocol also defines
delegate methods used by control delegates.
...
These include: controlTextDidBeginEditing:, controlTextDidChange:, and controlTextDidEndEditing:
Oh, wow - in adding more detail to my question, I think I figured out the answer. Instead of dragging from text field delegate to File's Owner, just drag to the blue cube that represents App Delegate!

Drag and drop in an NSView

I'm testing drag and drop in an NSView, but draggingEntered: is never called.
Code:
#import <Cocoa/Cocoa.h>
#interface testViewDrag : NSView <NSDraggingDestination>
#end
#implementation testViewDrag
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self registerForDraggedTypes:[NSImage imagePasteboardTypes]];
NSLog(#"initWithFrame");
}
return self;
}
-(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
{
NSLog(#"draggingEntered");
return NSDragOperationEvery;
}
-(NSDragOperation) draggingUpdated:(id<NSDraggingInfo>)sender
{
NSLog(#"draggingUpdated");
return NSDragOperationEvery;
}
#end
In interface builder I add a subView (class is set to testViewDrag) to the main window. In log I can see the initWithFrame log but when I drag nothing is shown in the log.
What am I missing ?
"To receive drag operations, you must register the pasteboard types that your window or view will accept by sending the object a registerForDraggedTypes: message, defined in both NSWindow and NSView, and implement several methods from the NSDraggingDestination protocol. During a dragging session, a candidate destination receives NSDraggingDestination messages only if the destination is registered for a pasteboard type that matches the type of the pasteboard data being dragged. The destination receives these messages as an image enters, moves around inside, and then exits or is released within the destination’s boundaries." You can read more about the drag and drop programming topic here. As I see it, your problem lies in the argument you define in your registerForDraggedTypes: method.
Try replacing it with this:
[self registerForDraggedTypes:[NSArray arrayWithObjects:
NSColorPboardType, NSFilenamesPboardType, nil]];
Hope this helps!

Load nib from other nibs (i.e. Interface Builder) in Cocoa

I have a view setup in Interface Builder which contains a viewController that loads another Nib file. However, for some reason the objects contained in the nib file being loaded never gets called awakeFromNib. What am I missing here? Is there anyway to load views from Nib in interface builder and also manage their properties and sizing in the interface builder?
In general, what are the best practices for managing multiple nibs and composing them into complex views?
Final Solution:
I created a NSViewController subclass like this.
#interface NibLoadingViewController : NSViewController
// The placeholder would be replaced during run-time by the view
#property (weak, nonatomic) IBOutlet NSView *placeholder;
#end
#implementation NibLoadingViewController
#synthesize placeholder = _placeholder;
- (void)awakeFromNib {
if (self.placeholder)
self.view = self.view; // Trigger lazy loading
}
- (void)loadView {
[super loadView];
if (!self.view)
return;
// Replace the placehoder if it exists
if (self.placeholder) {
// Copy over relevant attributes
self.view.frame = self.placeholder.frame;
self.view.autoresizingMask = self.placeholder.autoresizingMask;
self.view.autoresizesSubviews = self.placeholder.autoresizesSubviews;
// Replaces the old view
[self.placeholder.superview replaceSubview:self.placeholder with:self.view];
self.placeholder = nil;
}
self.nextResponder = self.view.nextResponder;
self.view.nextResponder = self;
}
#end
This way, you just need to hook the placeholder outlet in the nib that contains the view controller and it will automatically load the other nibs for you and copy all the attributes from placeholder over and replace it in the main nib.
The content of the nib-file is lazy-loaded. If you want -(void)awakeFromNib to be called, you need to access something from the nib-file first.
NSViewController *controller = [[NSViewController alloc] initWithNibName:#"MyView" bundle:nil];
/*
* awakeFromNib was not called yet
*/
NSView *view = controller.view;
/*
* but now -(void)awakeFromNib was called.
*/

NSWindowController subClass - Init is Called twice

im very new in cocoa development and I'm trying to load a Window.
I will explain my problem.
When the user click the menuItem I use the following code to load my window
if ( !cadastroContasController )
{
cadastroContasController = [[cadastroContas alloc]init];
[cadastroContasController SetMenuItem:sender];
}
if ( ![[cadastroContasController window] isVisible] )
{
NSLog(#"!isVisible");
[cadastroContasController showWindow:nil];
}
I my cadastroContas class looks like this:
#interface cadastroContas : NSWindowController
{
NSMenuItem *mnuCommand;
IBOutlet NSComboBox *cmbSelecao;
IBOutlet NSTextField *txtNome;
IBOutlet NSTextField *txtSaldoInicial;
IBOutlet NSTextField *txtAnotacoes;
}
- (void)windowDidBecomeKey:(NSNotification *)notification;
- (BOOL)windowShouldClose:(id)sender;
- (void)windowWillClose:(NSNotification *)notification;
- (void)SetMenuItem:(NSMenuItem*) menu;
- (NSMenuItem*) MenuItem;
#end
and the implementation is
#implementation cadastroContas
-(void)windowDidLoad
{
NSLog(#"windowDidLoad");
[mnuCommand setState:NSOnState];
}
-(id)init
{
self = [super initWithWindowNibName:#"cadastroContas"];
NSLog(#"Init self=%p", self);
return self;
}
-(void)dealloc
{
NSLog(#"Dealoc=%p", self);
[super dealloc];
}
- (void)windowDidBecomeKey:(NSNotification *)notification
{
NSLog(#"windowDidBecomeKey window=%p", [self window]);
}
- (BOOL)windowShouldClose:(id)sender
{
NSLog(#"windowShouldClose Window=%p", [self window]);
NSLog(#"mnuComando=%p GetMenuItem=%p", mnuCommand, [self MenuItem] );
if ( mnuCommand )
{
[mnuCommand setState:NSOffState];
}
return YES;
}
- (void)windowWillClose:(NSNotification *)notification
{
NSLog(#"windowWillClose Window=%p", [self window]);
NSLog(#"mnuCommand=%p GetMenuItem=%p", mnuCommand, [self MenuItem] );
[self dealloc];
}
- (void)SetMenuItem:(NSMenuItem*) menu
{
mnuCommand = menu;
}
- (NSMenuItem*) MenuItem
{
return mnuCommand;
}
#end
When the menu was clicked, I received two messages "Init" and I don't know why.
Exemple:
[2223:a0f] Init self=0x10014fe40
[2223:a0f] Init self=0x10011f5a0
The second message let the "[cadastroContasController SetMenuItem:sender];" useless.
So, I need help to understand whats going on..
Another thing, [[cadastroContasController window] is always returning NULL(0x0)!!, but inside my controller i can handle it (it isn't null).
This means you inited two instances, as shown by your logging of the self pointer: Notice that the value is different between the two messages.
You can use the Allocations instrument in Instruments to see what caused each window controller to be instantiated.
Usually, this problem happens when you create one of these in the nib and the other one in code. In the case of a window controller, the one you create in code should be the owner of its nib; you should not create another window controller as an object in the nib.
Another thing, [[cadastroContasController window] is always returning NULL(0x0)!!, but inside my controller i can handle it (it isn't null).
The window controller whose window outlet you set to the window is the one that is returning non-nil. The window controller whose window outlet you didn't set is the one that is returning nil.
Following from what I said above, after deleting the window controller you created in the nib, you should connect your File's Owner's window outlet to the window.

App crashing when setting delegate and datasoruce

If I want to set my delegate and datasource for my uitableview my app crashes. What I'm doing wrong?
- (void)loadView {
NSLog(#"AddListViewController");
ShareListViewController *shareListViewController = [[ShareListViewController alloc] init];
UITableView *shareListView = [[UITableView alloc]initWithFrame:CGRectMake(100, 30, 100, 200) style:UITableViewStylePlain];
shareListView.delegate = shareListViewController;
shareListView.dataSource = shareListViewController;
[self.navigationController.view addSubview:shareListView];
[shareListViewController release];
}
And my ShareListViewController
#interface ShareListViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource>{
}
#end
It also not working if I remove the protocols.
Thx for your help
You are releasing the shareListController controller. This is wrong, since both, that dataSourceproperty as well as the delegate property of a table view are declared as having retention policy assign, which means, that they do not retain their values. This is the responsibility of client application/view controller.
#property(nonatomic, assign) id<UITableViewDelegate> delegate
#property(nonatomic, assign) id<UITableViewDataSource> dataSource
See the UITableView Reference.
The best approach seems to be to declare an ivar in your view controller like:
#interface MyViewController: UIViewController {
... other stuff ...
UITableView* shareListView;
ShareListController* shareListController;
}
... more stuff ...
#end
In MyViewController's viewDidLoad (or, wherever else you set up the table view) alloc/init the shareListController as usual, but instead of releasing the object after you have set up the dataSource and delegate properties, you remember the reference (you still own it) in the shareListController ivar.
Finally, in the view controller's dealloc method, do:
- (void) dealloc {
... release other stuff ...
shareListView.delegate = nil; // Really, really defensive
shareListView.dataSource = nil; // here, but it won't hurt
[shareListView release];
[shareListController release];
[super dealloc];
}

Resources