I am new to Cocoa and I don't understand the concept of File's Owner of a .nib file.
Here is the way I would see things :
Consider a file myNibFile.nib file that describes how a window looks.
Now, I want to connect an actual window to this .nib file. So, I create a class myWindow, which is a subclass of NSWindowController. And, to do this connection, I change the init method like this:
-(id)init
{
[super initWithWindowNibName:#"myNibFile"];
return self;
}
So, I understand that when I create an instance of myWindow, the "system" will go and look at the .nib file and create the adequate object.
So, my question are :
why do I have to specify that the File's Owner of my .nib file is myWindow ? Isn't it redundant ?
I guess it means I didn't really understood what the File's Owner. What is it ? Why does the .nib file have to belong to something ? Can't it be "somewhere" in my "application" and when it is needed, the "system" goes there and use it ?
Thanks for helping me to see more clearly in these new concepts !
Two points to be remembered:
The File owner is the object that loads the nib, i.e. that object which receives the message loadNibNamed: or initWithNibName:.
If you want to access any objects in the nib after loading it, you can set an outlet in the file owner.
So you created a fancy view with lots of buttons, subviews etc . If you want to modify any of these views / objects any time after loading the nib FROM the loading object (usually a view or window controller) you set outlets for these objects to the file owner. It's that simple.
This is the reason why by default all View Controllers or Window Controllers act as file owners, and also have an outlet to the main window or view object in the nib file: because duh, if you're controlling something you'll definitely need to have an outlet to it so that you can send messages to it.
The reason it's called file owner and given a special place, is because unlike the other objects in the nib, the file owner is external to the nib and is not part of it. In fact, it only becomes available when the nib is loaded. So the file owner is a stand-in or proxy for the actual object which will later load the nib.
Hope you've understood. I'll clarify any of the points if you ask.
The fundamental thing to understand is that Interface Builder allows you to create objects that are automatically connected to each other, with no effort on the part of your program. You can instantiate all kinds of objects including non-view ones, and they can be inter-related; for example, you might create the instance of a table view data source along with the view itself, etc. This mechanism is commonly used to create an Application Delegate within your Main Menu NIB.
However, since it's all done via drag&drop, it seems there is no way that you can form a connection between any of the NIB objects and the objects that already exist in your application, with one exception.
When code is loading the NIB file, you have the option to specify exactly one object which the NIB will consider to be the "Files Owner". This is the placeholder that you see inside Interface Builder; since it can represent any object within your application, Interface Builder can't know what actions/outlets are available on it. This is why you can modify the "class" of the Files Owner, in the Attributes tab.
Files Owner does not really represent "ownership" or "parenthood". What it represents is "the object that loaded this NIB".
File's Owner is a placeholder in IB so all the outlets and actions in your code are "linkable" in IB, you can control-drag to connect stuff on the screen to the code.
File's Owner in Interface Builder is so that it knows the object type of the parent. This is used in two ways. Firstly, in Interface Builder so that IB knows what outlets and actions are available to you to connect. Secondly it is used by the application framework to know how to reconnect things to the rest of your code once the nib file is loaded.
Related
In a view-based single-column NSTableView containing a default NSTextField in its Table Cell View, I'm trying to listen to confirmed user edits by connecting the NSTextField's action, within an Interface Builder view of the .xib, to a method in my ViewController for the window. But at run time (during window initialization) I get "Could not connect action, target class NSObject does not respond to -textCellChanged". I don't understand which NSObject is being incorrectly targeted, and I have many other NSViews in the window correctly connected to other outlets and actions in the same WindowController.
I see various other posts with a similar symptom, often also in the context of NSTableView, and have explored the solutions or partial solutions to those other problems in my context without success. Is there any particular magic about wiring Table View Cells in Interface Builder that I am overlooking? Or perhaps phrased differently: how is the target object actually determined at runtime, when the action is simply a class method in the File's Owner (and when does this vary for different controls all wired to the same owner of a common superview)?
Here are some particulars of context:
File Owner is set to a subclass of NSWindowController and Module there correctly inherited to my app target.
Probably relevant: I am not using Storyboards, and the top-level object in my XIB's outline view hierarchy is a Window (NSPanel), rather than an View or View Controller. The NSWindowController only appears within the XIB as the File Owner (not as its own object in Outline View).
In any of the various wiring scenarios I've tried (following), Interface Builder "looks like" the wiring op has succeeded. After wiring, the File Owner's Connections inspector lists the intended connection under Received Actions ("textCellChanged: ... [x] Table View Cell"), along with many other actions connecting components in the NSTableView's superview to other methods in the NSViewController.
Likewise, connecting the NSTextField as an OUTLET in the same NSViewController works with no problem. It's only the action (or target/action?? in IB one only sets "action") that fails.
The File Owner is also the data source and delegate of the NSTableView, and the NSTextField is set to Action:Send on End of Editing and Behavior:Editable. I don't think any of these are relevant to the particular symptom, which is just a failure to connect an action.
The NSWindowController is Swift; I have tried implementing the appropriate action within both the main NSWindowController implementation or in an extension that implements NSTableViewDelegate to no noticeably different effect.
Other posts suggest Xcode bugs in wiring, though in older versions of Xcode (I'm in 10.2). Here are various approaches I've tried, all with similar results:
Ctrl+Drag from Table View Cell icon in IB Outline View to NSWindowController source module, targeting there either an existing #IBAction or permitting Xcode to generate a new Connection (type Action, Object: File's Owner) and with it a new method in the ViewController
Reverse Drag from Source Code "left-column radio circle" next to an #IBAction to the Table View Cell in Outline View of my .xib
Ctrl+Drag from Table View Cell icon (in Outline View) to Placeholder/File's Owner icon, then choosing an appropriate action method from the pop-up list of methods implemented in the view controller.
Possibly some others
Finally, here are some related posts and how they differ:
This sounds like an identical symptom, but in comments OP claims to have fixed the issue by a combination of setting File Owner to the View Controller (done) and by working around blocking XCode bugs (not visible in my context).
This suggests I'm linking to a stale (deleted) method; definitely not the case when I allow Xcode to create the method for me.
This unanswered post suggests the user gave up on the situation as an IB bug and gave up in preference for a non-target/action workaround. I suppose I could pursue listening to notifications on the NSTextField as a similar workaround.
Finally, the accepted answer to a similar symptom here is that the connection to File Owner is incorrect in that case, where File Owner was the NSApplication object rather than the View Controller. In my case, File Owner is the View Controller object that defines these methods, so feels like the correct target.
Any stone unturned here? Thanks in advance for your help.
The file's owner in a xib file is a placeholder object representing the owner of the nib and representing the owner object passed to makeView(withIdentifier:owner:). Pass the owner of the nib (usually the view controller or window controller) to makeView(withIdentifier:owner:).
I need my class, call it Document, to be the file owner of a nib containing the user interface (just as Document: NSDocument is the file owner in the Apple's document-based template). Likewise, I do not want Document to inherit from NSWindowController, but to manage an instance of it that will load the nib and set its Document as the file owner:
let windowController = NSWindowController(windowNibName: "DocumentWindow", owner: self)
Sounds simple, yet I am unable to get the file owner to deallocate along with all the other nib objects. The reason for this is that, according to Apple, "For historical reasons, in OS X the top-level objects in a nib file are created with an additional reference count." (see: Top-level Objects in OS X May Need Special Handling)
If I set the window's isReleasedWhenClosed to true and set the property itself to nil, then its deinit is called. Similarly with the instance of the NSWindowController... but the instance of Document, the file owner, remains! It's deinit is never called.
If I set the file owner to be the window controller, everything deallocates fine, including the Document instance, but this setup is difficult to subclass, which is what I need... I need the Document instance to be the file owner.
And so, for my case, the documentation suggests the following:
If the File’s Owner is not an instance of NSWindowController or
NSViewController, then you need to decrement the reference count of
the top level objects yourself. You must cast references to top-level
objects to a Core Foundation type and use CFRelease.
But how can I do this in Swift?
Would be grateful for any insight and suggestions!
In my Cocoa app, I have a sheet with a one-column NSTableView that lists a bunch of files in a directory (the app makes back-ups of it's main database, provides this list to users so they can revert to a particular back-up). The content is loaded into and provided to the table view by an NSArrayController, each object is just an NSFileWrapper (I'm considering using NSURL instead, but I digress). The NSArrayController handles sorting, enabling the buttons when a row is selected via bindings, that's all great. I have an NSWindowController subclass object (BackupsSheetController) that hooks all this up and exists in the sheet's nib.
However, when a user edits one of the cells, I want to respond to that change from BackupsSheetController by appropriately re-naming the file represented by that cell, putting it in its new location. Since the table view is bound to the NSArrayController, I don't get sent the NSTableViewDataSource message – tableView:setObjectValue:forTableColumn:row:. If I set my BackupsSheetController as the datasource for the NSTableView object in the nib, I get sent that message sometimes, but not very often, to say nothing of every time.
Most questions and examples I see out there for this scenario handle this all by using a custom model class for items in their table view, and make some controller object an observer for changing properties that they wish to respond to. In other words, each item would be something like a BackupNode object, and BackupsSheetController would observe each for changes to the name property (or whatever I would call it). That seems totally overkill for my scenario, but I also don't want to ditch the bindings I've already got in use and I don't see another way to do this. Is there another way to do this, to make sure I reliably get the setObject:... message? Or should I drop the NSArrayController and make BackupsSheetController the delegate and datasource for the table?
In the "BackupNode" scenario, I don't see why BackupsSheetController would observe each for changes in its name. That's a very roundabout way of doing things. I would think that the hypothetical BackupNode object would simply do the necessary work in its setter for the name property.
Anyway, I recommend using proper model objects. When you try to build a model with only Cocoa-provided objects like NSFileWrapper, NSURL, or NSMutableDictionary, you end up doing more work in the long run than if you just make a proper model object.
On a tangential topic, why is your window controller in the NIB? It should be the thing which loads (and owns) the NIB, which of course requires that it exist prior to the NIB being loaded, which means it can't be instantiated in the NIB.
Is this a good practice, to have one xib (Document xib), and many nswindowcontrollers and nsviewcontrollers.
This is better because, you don't have to bind properties between multiple xibs
But what about cons?
Cons are:
You have to load the entire nib, so even if you only need one of the windows, you have to load them all.
Complex nibs can get unwieldy.
The "File Owner" is your document instead of your window controller, which encourages you to circumvent the window controllers, binding and connecting the view directly to the model.
When you have one nib per window, the file owner is usually the window controller, which brokers access to the document or exposes it as a property. Sometimes you do want to bind e.g. array controllers and object controllers to the document, but accessing it through the window controller gives you an opportunity to monitor the dependencies.
First, some background: I am trying to implement a master-detail interface in Cocoa (for OS X). That is, I have a window with two NSTableViews that display two different types of objects. For this question, let's say they are warehouses and packages (to pick an example that is analogous to my actual problem.) Selecting a row in the first table view (on a warehouse) will display a list of packages which belong to that warehouse in the second table view. For the model part, I currently have an NSMutableArray called warehouses of warehouse objects, and each warehouse object has an NSArray of package objects. One thing to note is that the warehouses variable is modified after the NIB files are loaded, so the NSArrayController has to be notified.
Now, I've tried to organize it so that the "master" is in its own view object, and the "detail" is in its own view object. This means that there are three NIBs: a WarehousesView NIB, WarehouseDetailView NIB, and a MainWindow NIB.
The WarehousesView NIB contains an instance of a WarehousesViewController (subclassed from NSViewController) and the view itself.
The WarehouseDetailView NIB contains an instance of WarehouseDetailViewController and the view itself.
The MainWindow NIB contains the main window, an instance of MainWindowController, and an instance of both WarehousesView and WarehouseDetailView. The window itself contains an NSSplitView, and the views of the split view are connected to the corresponding view instances in the NIB file.
That brings me to the first half of my question:
1) Is this a good way of splitting up the application views for a Cocoa application? To me it makes sense, because at a later point more details about the warehouse besides a list of its package inventory might be added to the WarehouseDetailView.
It's an important question, because everything works just fine if I skip creating views, putting all controls in the window directly and put everything else, including NSArrayController instances corresponding to Warehouses and Packages, into the same NIB file. I don't need to ask the second half of the question if I shouldn't even be doing it this way.
The second half of the question is basically:
2) Where should I place the NSArrayControllers corresponding to Warehouses and Packages if I split it up as described above so that the master-detail interface still works? Currently I am using Cocoa bindings, so somehow the content array of the Warehouse NSArrayController needs to bind to my warehouses array, and the content array of the Packages NSArrayController needs to bind to the selection of the Warehouse NSArrayController
I've tried a few things, and I couldn't get anything to work completely. Specifically, I've tried putting the NSArrayController for Warehouses into the WarehousesView NIB and the NSArrayController for Packages into the WarehouseDetailView NIB. The problem with this approach is that I cannot figure out a way to bind the Package NSArrayController to the selection of the Warehouse NSArrayController. The other thing I've tried is (1) putting both NSArrayControllers into the MainWindow NIB, (2) connecting those NSArrayControllers to IBOutlets in the MainWindowController, then (3) passing those variables to their respective view controllers via their constructors, (4) exposing them as properties in the view controllers via KVC, and (5) binding the necessary table columns in a view to the array controller through the File's Owner. The result was that nothing appeared, but there were no errors either. If one of these approaches is the preferred way to do it, I can give more details to help see if I am doing it incorrectly.
Thanks in advance!
Edit: I did look at this related question, and they seemed to be using separate instances of NSArrayControllers for each NIB file if I understood it correctly, and that didn't seem to make sense from a design point of view, but perhaps I am wrong?
Part 1: You can certainly do this. I'd say it's a matter of preference. Personally, then, if the views were going to be displayed simultaneously in a window, I would keep them in the same nib.* Modularity is also a good thing, though.
Part 2: You can put the array controllers wherever you like, really. The only thing you need to worry about is getting each object the references that it needs to the information you want it to have. If you want my 2¢, I'd say put each in the nib with the view its contents will be displayed in. That'll make your detail view setup more difficult, but it continues the modularity you seem to be going for.
You have to remember that every object in the nib is a real instance. The nib allocates and inits them for you; if you put a MyClass object in one nib, and a MyClass object in another nib, those are two different objects. This is sometimes a tricky thing about nibs: it's really convenient to have instances automatically created for you, but it also means some fiddling around with references when you want to do things across nibs.
It sounds like you put instances of WarehouseView and WarehouseDetailView into both your individual nibs and MainMenu.nib and expected them to be the same objects. It won't be so. You have to link objects in the nibs to objects that they already know about. You'll have to work this out for your particular situation.
I don't know where your model is stored, or how you're getting the nib loaded. Whichever object it doing that loading, though, will likely be your link between the individual nib and the rest of the app. This is what the File's Owner proxy object in nibs is for -- it gives you a place to hook up objects in the nib to code that they wouldn't otherwise know about.
*: If you find it easier to layout the views if they are not enclosed in the split view in IB, you could set them up on their own: put the custom view objects in the MainMenu.xib window and you can open each view in its own IB window (though it will not be in a window in the app). Then set the split view's subviews in something's awakeFromNib.