Cocoa - How to bind width of a NSView in InterfaceBuilder? - cocoa

I'm trying to create a simple application that draws a grid in a custom view.
The custom view size is fixed (it doesn't depend on the size of the window).
The custom view is embedded in a scroll view to be able to explore the grid when the scroll view can't display the entire custom view.
Now i want to add sliders for controlling the grid parameters (nb raws, nb columns, tile width, tile height, ...), and these parameters influence the size of the custom view.
As an experiment, i'm trying to bind one slider's value to the width of my custom view but fail to find a way to do it.
How am i supposed to do this sort of things ?
Is it possible to do it in InterfaceBuilder ? I expected to find a width binding in Bindings Inspector Window but it's not there, curiously ;-)
Thanks.

You can't bind the width of a plain NSView, and binding to a property of a view is always a bad idea. View properties are seldom observable. Moreover, there is no width property; it's one member of the structure that is the value of the frame property, which you must set all at once or not at all.
As for exposing bindings in your custom view, you can do that, provided you keep the properties observable (which consists of little more than only changing the property's value using its setter). You'll need to expose the bindings in your view class's initialize method, and you'll need to write an IBPlugin.
See also the Cocoa Bindings Programming Topics.

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.

Validating a drag to an NSCollectionView isn't reflected visually

I have an NSCollectionView that I want to accept items dragged from elsewhere in my application.
I implement collectionView:validateDrop:proposedIndex:dropOperation: and collectionView:acceptDrop:index:dropOperation: in the collectionview's delegate and register for the appropriate dragged types. Both methods get called fine when I drag the appropriate types, but I don't get a blue focus ring over the collectionview indicating a valid drag.
Have tried both the collection view and its containing scroll view on Default and External settings for the focus ring. Both are just the standard non-derived Cocoa classes. Wondered if there was anything else I should try. Surely it isn't necessary to subclass NSCollectionView for this?
Thanks
Chris
Focus rings are not typically the correct way to provide feedback about drag destinations. Every view does it slightly differently. NSTextView shows the insertion bar. NSTableView shows a blue line in between rows for Before drop operations, and shows a bezel around the row for On drop operations. (See NSTableViewDropOperation)
NSCollectionView shows a "gap" between existing subviews to show where the items will be dropped for Before drop operations, and it will set the selected property on NSCollectionViewItem to YES for On drop operations. (Note: NSCollectionViewItem doesn't do anything by default to visibly represent the selected property. You must implement that yourself.)
Since NSCollectionView's feedback uses existing subviews only, it appears there isn't any feedback at all for empty NSCollectionView's. You would need to subclass to provide this behavior yourself. You could also file a bug to request that NSCollectionView do this itself.

Cocoa: how to implement a custom NSView with an editable text area?

What's the minimum implementation needed to make a custom NSView with an editable text area? I assume NSTextFieldCell can be used for this. I've succeeded in drawing the cell in the view (which is straightforward), but making it editable seems to require a more complicated coordination between the view and the cell. Is there sample code available somewhere?
Update. I should have made clear that my longer-term goal is to have many more editable text areas on the same view. AFAIU it is better to use cells in that case as they are more light-weight than full-blown views. My updated question is: What's the minimum implementation needed to make a custom NSView with an editable text area using an appropriate NSCell?
What's the minimum implementation needed to make a custom NSView with an editable text area?
Make an NSView.
Put an NSTextField in it.
Remember, NSViews (custom or otherwise) can contain other NSViews, and an NSTextField is a kind of NSView.
If you don't want code outside the custom view class to know about the text field, and it probably shouldn't, the custom view can create the text field and add it to itself as a private implementation detail. To do this, simply don't expose the text field in the custom view class's #interface (aside from the instance variable declaration, which is unavoidable).
The custom view should, of course, not draw wherever it put its text field. It could draw there, but the text field would cover it.
I assume NSTextFieldCell can be used for this.
Yes, if you don't mind reimplementing NSTextField. Adding an NSTextField as a subview of your view is much easier.
If you want to make a grid of text fields (with a dynamic number of them, perhaps), use an NSMatrix of NSTextFieldCells. You can, of course, add the NSMatrix as a subview of your custom view.
If you want to edit a text cell just call editWithFrame:inView:editor:delegate:event: on the cell object. This method requires the NSEvent that started the editing, so you can only call this from an event handler. There also is selectWithFrame:inView:editor:delegate:start:length: which sets up the field editor with an selection. You can use this if you need to start the editing from outside of an event handler.
After the user is done editing you need to call endEditing: on your cell.

How can my code get notified when the user pastes or drags an image into NSImageView?

I have an instance of NSImageView. I have told InterfaceBuilder to allow the user to paste or drag an image into this view. This works. However, I don't see a way in the NSImageView or related documentation to get notified when this actually happens. I was expecting a delegate or some such, but I have been unable to find it. Any ideas?
This is what Cocoa Bindings does best.
Instead of talking to the view, simply have a property whose value is an image, and bind the image view's image binding to it; then, when you want to change the image in the image view yourself, all you have to do is set the value of the property, and the image view will notice the change.
The user pasting or dragging into the image view is essentially the same thing, partly in reverse: The paste or drop will change the value of the image view, which will then set the value of your property, which will cause anything else bound to it to notice the change and pick up the new value as before.
Aside from ripping out your existing send-explicit-messages-to-views code, this will require almost no code work: The only code you need is for your property. You'll hook up the Binding in IB.
See the Key-Value Coding Programming Guide and the Cocoa Bindings Programming Topics.
As for me the better way to override function
-(void)paste:(id)sender
But only if you have it in NSView based class which has NSResponder as parent

Resources