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.
Related
I'm trying out Cocoa (on OS X Mavericks) with a NSDocument-oriented app with a (planned) WebKit control. I noticed that the "New" menu command is disabled if all the document types are Viewer or Other. I'm making a web browser, so the app shouldn't be an Editor for anything, yet that's what I need to enable the New command.
I guess I need to override something, but where? The obvious candidates are the NSDocument subclass, a NSDocumentController subclass, or an application delegate. The first one is the only one I actually have so far (i.e. included by Xcode's default code), but it doesn't seem appropriate for NSDocument instances to create new ones. So I would have to create a NSDocumentController or NSApplicationDelgate subclass to do this, right?
So much of a newbie that I didn't completely RTFM.
The new-document command is handled by NSDocumentController. That class has a defaultType method that's used to determine which type a new document should be. By default it chooses the first type in the (static) list of document types that is set to "Editor" mode. It returns nil otherwise (i.e. Viewer and Utility apps).
A non-editing app can gain new documents by creating a subclass of NSDocumentController that at least overrides defaultType to return a non-nil string describing the document type for new documents. I used a MIME type, but I guess UTIs (and maybe extensions) also work.
But there's a new problem: your NSDocumentController won't be created by default; the default App setup ignores the class and makes a NSDocumentController object anyway. There's supposedly two ways around this: put an object of your subclass into the app's first NIB, the one with the main menu bar; or directly set the document-controller attribute to a new object of your subclass in one of the initialization methods of the application delegate. (Since the default document-based project templates don't include an application delegate class, you have to create that too.)
But I heard that either method is uncertain; if any previous part of application startup touches the NSDocument system, a default document-controller will be created and block out your object. Are there any Cocoa gurus out there that know where to put a custom NSDocumentController subclass object so it can't get pre-empted by a default document-controller?
...
I tried putting what I said in the third paragraph to work, but it doesn't. I keep getting "Date Time MyApp[Number1:Number2] The XXX type doesn't map to any NSDocumentClass." from the NSLog debug screen, where XXX is the string I return from defaultType. Tried both MIME types and UTIs. I'm stumped for the moment.
I’ve been reading through several books on Mac development, but cannot find the information I’m looking for.
The books all describe how to make floating windows or panes, but never mention how to make them all in one window. A simplified example of what I’m looking to create is shown below:
Basically, there will be three windows; A selector window with radio buttons to choose which NSDocument is currently being used, a window underneath that with buttons that show different windows to the right that allow viewing and manipulation of certain data.
For a example, each NSDocument may have a color value that can be set in the window shown by clicking view A, and some text strings that can be set in the window shown by clicking view B.
So the questions are:
Is it appropriate to use a single NSDocument sub-class for each Doc #1 and Doc #2?
Which classes should I use to set up the application as shown? NSWindowController? NSWindow? NSPanel?
I’m only looking for guidance on what to read up on, so any pointers are appreciated.
EDIT:
To clarify this further, I want to have a table view where the buttons are (View A & B), and by clicking them they will cause the other window/view to change it's contents.
It's like the split view in the iPad settings application, there is a table view on the left, and when it's pressed the right side changes.
The radio buttons are there only to illustrate that I want more than one Document. I'm guessing I need more than one to handle this? Or perhaps I should place them all in a single NSDocument? Somehow that doesn't seem right.
To achieve what you want you need one window (NSWindow), one window controller and various views each with their own view controller. There are several ways you could set this up, all depending on your requirements:
You'd have at least 3 views (instances of NSView): one for the selection of the document class, one for the view selection and one for the content. Each view is controlled by a view controller (instance of NSViewController). Additionally you can opt to wrap the views in split views (NSSplitView) so your user can resize the real estate available to each view.
You have one window with a window controller. If you choose a Document based app template in Xcode, Xcode will generate a subclass of NSDocument which you can use as your window controller (or choose to use Core Data and Xcode will generate a subclass of NSPersistentDocument with all bells and whistles you need to access Core Data for document persistency).
So to come back to your questions:
1: Yes, but depending on your requirements. If Doc #1 is a completely different thing than Doc #2 than you might need to re-evaluate. For example Doc #1 might have completely different persistent requirements than #2.
2: There's no single scenario here, but one that worked for me: Take the project template for a document based app (with or without Core Data). Use the generated subclass of NSDocument (or NSPersistentDocument) as your window controller. Use NSView to implement the views in your window where each view is managed by its own controller, which is an instance of NSViewController.
I know this is an old question, but a way to do it how you want would be to use: ContainerViews and set their embed segue to be the view controllers you want.
I've been learning about when to use WindowController and when to put stuff in the Document object. Looks like Document can work fine as a Controller if you have a simple interface. I have a simple interface in my application, but is it a good practice to put IB outlets into WC anyway? What would be a scenario when you would NOT want to use a WC?
Here are some scenarios:
A document object you intend to use with multiple windows (as commenter noted above) or having different views
A document object you could potentially open and process without displaying a window at all
An application with so much controller code that it's difficult to manage with a single class
During window initialization, the the document will instantiate the window controller.
After that happens, part of the point of separating the model controller and view controller is removing the document's dependency on the window.
As you refine your design, take a look at places where the document needs access to the window, and consider whether you can implement that functionality a different way, for example, by handling it in the window controller instead of the document.
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 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?