Custom NSViewController representedObject doesn't update when view selection changes - cocoa

I've got a custom NSViewController that's also an NSOutlineViewDataSource. I also have a window with an NSOutlineView bound to an instance of my view controller as the data source, and the NSOutlineView bound to the view: property of the custom view controller.
The controller fills the outline view fine. However, selections within the view don't automatically update the representedObject property of the controller. The only thing that seems to trigger a change in representedObject is when I call setRepresentedObject: directly... which sort of defeats the purpose.
Any idea why that might be? representedObject is supposed to update automatically, right? Thanks in advance...

If I understand your question, you are not understanding what representedObject is for. The representedObject is the model object that represents all the data that a view controller is displaying. It is not the current selection of a view showing a collection of content. The representedObject for your custom top level view controller would be an NSArray or other collection class which contains the set of data you are displaying in your view controller. Changing the representedObject for your custom top level view controller should have the effect of swapping out for an entirely new data set if the pattern is being used correctly.
Typically the view controllers for the individual elements in a tabular type view would each have their own representedObject and this object would not change. For example if you were using an NSCollectionView each element in the view is controlled by an NSCollectionViewItem which is a subclass of NSViewController. The representedObject for each NSCollectionViewItem tells it what data to present in its little view.
NSOutlineView and its superclass NSTableView are different in that their cells are often NSCells rather than full fledged NSViews unless you choose to use them this way in Mac OS X 10.7 or newer. Even in this case, you don't typically have an NSViewController subclass managing each cell. So the view controller representedObject pattern is not used at the level of the individual element that the user would select. If what you want is to track the NSOutlineView's selection, there are many NSTableView methods that let you do this.

Related

How to share model object between view controllers?

I created a basic OS X application with a storyboard to just draw circles in a custom view. The main window contains a NSSplitViewControllercontaining two sub-views (content and side bar like Apple Pages or Numbers have). The content view is a custom subclass of NSView for drawing circles while the side bar view contains standard controls. Both should be bound to a model object which holds the properties like number of circles, diameter and so on.
As I understand both subviews have their own controllers in any case. How do have a data model object (let's call it Circles) which both controllers reference so I can hook up key-value observation for redrawing my custom view on changing the controls' values?
My idea would be to create the model object in the common parent controller and pass it on to the children, but how to set that up in Interface Builder in Xcode 7.2?
Working off my comment. You can use the representedObject property of NSViewController to pass any object along to other view controllers. The one downside to this though is that the property is of type AnyObject? so making that work with Swift can be awkward. In your NSViewController subclass you can make a new property to store the data and give it the correct type, or you could make a protocol to define the computed property that serves as a wrapper around representedObject property.

NSOutlineView with manual Cocoa bindings

I have an NSOutlineView that is bound to a NSTreeController. The details are like this:
the treeController has class mode, and CommonListData as the Class Name, which has a "children" property
the treeController also has "content array" bound to File's Owner's "headersArray" (of type NSArray). The "headersArray" is an array of CommonListData items
the NSOutlineView has bindings for "Content" to the treeController's arrangedObjects
the view-based outlineView is designed in Interface Builder, with each cell view has lots of elements. Each element (labels, images etc) have 'value' bindings to the NSTableCellView with the relevant objectValue.xxx model key paths
The setup is fairly simple, and it all works fine in adding and deleting objects. But the problem is that it doesn't use any of the NSOutlineView / NSTableView animations when adding or removing items from the outline view. The table just reloads itself if I add or remove any element to the treeController, or directly to the headersArray's children objects. I'm not sure if that is a limitation with Cocoa Bindings or what.
In any case, I am consider whether I can disable all the bindings and do this manually. The only drawback is that the NSOutlineView cell views have complicated elements on them, and I don't want to have to map the view elements to the data in code. Ideally it should keep the bindings in the NSTableViewCell.
So my question is:
Is it possible to keep the bindings within Interface Builder in a NSTableCellView, but not bind the content to the treeController? Or is there an alternative to binding "arrangedObjects", such that adding and removing data from the NSTreeController doesn't trigger an update to the UI?
It would help to have some more control over the bindings, so that I can trigger the animations correctly, and not have it just jump around with every update.
I believe the bindings of the views within the cell view are independent of the bindings of the outline view's content. Whether the outline view uses bindings or not, it eventually sets the objectValue property of the cell view. So long as that's done in a KVO-compliant manner (which it is), any bindings to that property will work.

NSTableView & CoreData: Delete Object at clicked row

I am pretty new to Core Data and am currently working on a small (OSX) app that uses an NSTableView to organise objects. I would now like to delete a row/object with the click of a button on that targeted row.
I access the managed object within the table controller by calling [NSApp managedObjectContext] (still trying to figure out that dependency injection thing) but I can't easily delete an objectAtIndex: like I used to with the array (which has now been replaced by the core data stack, right?).
How do I identify the object to be deleted? And consequently, how can I cleanly remove it from the stack?
This is probably a really basic question but I couldn't find any resources on it. Bindings obviously don't work because the row does not get selected before the click occurs.
Any help is much appreciated!
Bindings would work, in that you could have the button's IBAction query the objectValue for the parent NSTableCellView. Once you have that objectValue, you could call the bound arrayController to delete the object, and then the cell/row would disappear.
So, if you have a tableCellView that has a delete button with an IBAction, within that IBAction, you could get the sender's superview, ensure it's an NSTableCellView, get the objectValue, and call [myArrayController removeObject:...]
As it says in the NSTableCellView class reference:
The objectValue is automatically set by the table when using bindings or is the object returned by the NSTableViewDataSource protocol method tableView:objectValueForTableColumn:row:.
This is actually a typical pattern with views in cocoa. objectValue or often representedObject are properties on the views that refer to the data model objects they represent, so if you have a view pointer from sender on the IBAction, you can get the related data model object. And if you're using bindings and a controller, you can then just have the controller remove that object.
With bindings, you will often create buttons that need IBActions attached, rather than some direct binding. But those IBActions can most definitely interact with the controller and not the view.
And with core data, array controllers are really slick vs. assuming you have to do it all programmatically.

Bindings for custom views for NSCollectionViewItem

I've got a NSCollectionView bound to my array of model objects, the NSView prototype to render items of the collection view is set up to use my custom NSView subclass.
At runtime a (generic) view is shown/instantiated in the collection view for each array element, all good.
However, I just can't figure out how to get to my array objects from the individual view instances to render the actual data that's specific to each element in the array.
I.e. how are we supposed to hook up data to the NSView prototype that is used to configure a 'cell' in the collection view?
Outlets don't seem to work for that particular view; they're all nil at runtime.
Bindings don't work with a vanilla NSView (apart from hidden and tooltip bindings for vanilla views..) - and we cannot add new bindings that would show up in IB for our NSView subclasses, can we?
Any hint appreciated!
Nevermind - I've settled with a custom NSCollectionView class overriding only
- (NSCollectionViewItem *)newItemForRepresentedObject:(id)object
to access the item view of NSCollectionViewItem instances created by super and set the required property there.

Subviews of a NSCollectionViewItem's view are always nil

I have a very basic setup with a NSCollectionView. I have a subclassed NSCollectionViewItem which is used as the itemPrototype for the collection view. The collection view item has a view.
Using Interface Builder, I added an NSImageView to the collection view item's view and connected an IBOutlet to the instance of my collection view item subclass. The property for the IBOutlet is uses retain for its memory management.
My goal is to hide or show this image view whenever the selection state for the collection view item changes. But within the setSelected: method my property for the image view is nil, even though I am 100% sure its outlet is connected, its property is set to retain and i have not unset or released the image view.
I had a similar issue with collection view items where an activity indicator added to the view was always nil too. Why does this happen and what do I need to do to fix it?
As far as I now, IBOutlets pointing to non-top level objects in nib/xib file should be defined (weak).
Are you sure you have changed the class of NSCollectionView item correctly?
When breaking in setSelected:, and doing a po self in the debugger console, are you seeing the name of your class?

Resources