How can I determine the window controlled by an instance of NSWindowController ? According to Apple's docs, sending the controller the 'window' accessor message returns "The window owned by the receiver." But when I use it on an instance loaded from a NIB via initWithWindowNibPath:owner: it always returns 0.
I've searched unsuccessfully through Apple's AppKit docs for another message that might work better. (It's possible that a function that returns the content _view for the window in question would also work, since my ultimate goal is to be able to access the contained Views via their Tag fields.)
Maybe I'm misunderstanding the purpose of this window accessor? If so, is there a method that will allow me to determine the actual window controlled by my NSController instance?
thanks
Related
I have a NS(Persistent)Document.
I run my application via Xcode, then create a new document (without any data), and then quit my application.
In this case, I can check that the init and windowControllerDidLoadNib: are called
If I re-run my application with Xcode, then the previous document is automatically open.
But, I can check that neither init nor windowControllerDidLoadNib: are called.
Why is it so ?
What you're seeing is window restoration. As that document (part of the Document-Based App Programming Guide) puts it:
The document architecture implements the following steps in the window restoration process; the steps correlate to the numbers shown in Figure 5-2:
The NSWindowController method setDocument: sets the restoration class of document windows to the class of the shared NSDocumentController object. The NSWindow object invalidates its restorable state whenever its state changes by sending invalidateRestorableState to itself.
At the next appropriate time, Cocoa sends the window an encodeRestorableStateWithCoder: message, and the window encodes identification and status information into the passed-in encoder.
When the system restarts, Cocoa relaunches the app and sends the restoreWindowWithIdentifier:state:completionHandler: message to the NSApp object.
Apps can override this method to do any general work needed for window restoration, such as substituting a new restoration class or loading it from a separate bundle.
NSApp decodes the restoration class for the window, sends the restoreWindowWithIdentifier:state:completionHandler: message to the restoration class object [in this case, the document controller's class —Peter], and returns YES.
The restoration class reopens the document and locates its window. Then it invokes the passed-in completion handler with the window as a parameter.
Cocoa sends the restoreStateWithCoder: message to the window, which decodes its restorable state from the passed-in NSCoder object and restores the details of its content.
[Figure 5-2, and a paragraph explaining that views, other responders, and the document get saved and restored, too]
When the app is relaunched, Cocoa sends the restoreStateWithCoder: message to the relevant objects in turn: first to the NSApplication object, then to each NSWindow object, then to the NSWindowController object, then to the NSDocument object, and then to each view that has saved state.
The window restoration protocol is used for non-document-related windows, too, but the document machinery handles most of the dirty work for you. If you need to do anything on either side (probably both sides) of window restoration, override encodeRestorableStateWithCoder: and restoreStateWithCoder: in your document. The former is where you save transient information like selections, and the latter is where you restore that information in the resurrected document and its window(s).
The presence of coders implies that the document is initialized using initWithCoder: rather than init, though this isn't a documented fact (in the context of window restoration) that you should rely upon.
I'm a newbie and wanted to know if I'm on a relatively correct path.
I have a CoreData application, but not Document based (it's not a document but a central DB with many window/view possible).
Two type of window exist (B and T) to view different aspect of the data.
Since it's possible to open many B or T windows simultaneously (to show different part of the same aspect) I decided to create two subclass of NSWindowController (B_Controller, T_Controller) each with his own nib file (B.xib, T.xib).
In my AppDelegate, whenever some menu/shortcut is activated I allocate the corresponding controller, initWithWindowNibName:, show the window and enabled the File->Close menu.
Questions:
Is it correct to have one controller per window ? Or better/possible to have only one controller for all window of the same type ?
vague newbie question. Maybe this response:
NSWindowController is for one window only.
if you want a central place for all window use your AppDelegate.
if you want a central place for all window of some kind, create new class to do just that.
I'm developing a document based app. Each document has three windows (and hence three window controllers). I'd like to set it up so that two of the three windows are shared between different open documents (swapping views as needed). Is this possible? Can anyone point me in the right direction (documentation or examples)?
Thanks!
In that case, these shared window controllers should not be owned by any document (since each document would then have its own pair of the “shared” windows), but should be independent, probably owned by the app delegate or the document controller. You may also want to make the windows panels, as an Inspector would be.
You'll want to have each controller track which window is main, and update its window accordingly when the main window changes, because the new main window may have a different document.
Pretty much any tutorial on how to make an Inspector window will help you here.
It looks like you need to override -makeWindowControllers in your NSDocument subclass to create the controllers you want, invoking -addWindowController: on the NSDocument subclass to add your shared window controllers.
I haven't yet had to do this, but those are the methods I'd be looking at.
From Apple's NSDocument class reference:
makeWindowControllers
Subclasses may override this method to create the initial window controller(s) for the document.
- (void)makeWindowControllers
Discussion
The base class implementation creates an NSWindowController object with windowNibName and with the document as the file’s owner if windowNibName returns a name. If you override this method to create your own window controllers, be sure to use addWindowController: to add them to the document after creating them.
This method is called by the NSDocumentController open... methods, but you might want to call it directly in some circumstances.
It is possible, but it'll take a non-trivial amount of work from your side. In summary here's what you need to do:
Override setDocument: in the window controller and maintain the associations that it has to each document.
Make sure that each window controller (NSWindowController) disassociates itself from the document before the window is closed. The same goes for each view controllers that may be handling views inside the window.
Subclass the document controller (NSDocumentController) and take care of document closing to make sure that multi-document windows are detached from documents before any of the documents are closed. NSDocumentController is a singleton and thus you need to add an instance in your MainMenu.xib file to replace the default one.
You can read my step-by-step guide how to add support for multi-document window controllers here.
In Cocoa, how do you define a class that sends an action? I want to be able to connect the action to the selector of another object in IB in the style of NSButton. I would prefer not to subclass NSControl if possible.
Give it a property (informal is fine) holding an id, for the target. I'm not sure whether this should retain or not; I'd say no, since the target will normally be the controller that owns the window that (indirectly) owns the view.
Give it a property (informal in fine) holding a SEL, for the action.
Respond to mouseUp: and keyDown: (checking that the key in question is the space bar or return or enter) by sending yourself an accessibilityPerformAction: message, passing NSAccessibilityPressAction.
Respond to the accessibilityPerformAction: message by either sending your action message to your target (NSAccessibilityPressAction) or calling up to super (other), as described in the documentation for that method.
You should also implement the rest of the NSAccessibility protocol while you're at it. Test that work with a mix of the Accessibility Inspector and VoiceOver.
I am building a Cocoa desktop application. I want to know when a NSView's isHidden status has changed. So far using target/action doesn't help, and I can't find anything in NSNotification for this task. I would like to avoid overriding the setHidden method, because then I'll have to override all the NSView derived class that I am using.
UPDATE: I ended up using KVO. The path for "isHidden" is "hidden", probably because the setter is "setHidden".
You could use Key-Value Observing to observe the isHidden property of the NSView(s). When you receive a change notification from one of these views, you can check if it or one of its superviews is hidden with -isHiddenOrHasHiddenAncestor.
A word of warning: getting Key-Value Observing right is slightly tricky. I would highly recommend reading this post by Michael Ash, or using the -[NSObject gtm_addObserver:forKeyPath:selector:userInfo:options] method from the NSObject+KeyValueObserving category from the Google Toolbox for Mac.
More generally, one can override viewWillMoveToWindow: or the other related methods in NSView to tell when a view will actually be showing (i.e. it's window is in the window display list AND the view is not hidden). Thus the dependency on KVO for the 'hidden' key used above is removed, which only works if setIsHidden has been called on that view. In the override, 'window' (or [self window]) will indicate whether the view is being put into a visible view hierarchy (window is non-nil) or being taken out of it (window is nil).
I use it for example to start/stop a timer to update a control from online data periodically - when I only want to update while the control is visible.
Could you override the setter method for the hidden property so that it will trigger some custom notification within your application?