Re: Binding not setting model property
Re: Binding not setting model property
- Subject: Re: Binding not setting model property
- From: Ken Thomases <email@hidden>
- Date: Sun, 19 Jan 2014 04:05:06 -0600
On Jan 18, 2014, at 9:45 PM, Roland King wrote:
> That was one point of confusion for me right at the start. IB binds the 'value' property but as far as I could tell, NSTextField doesn't have such a property, calling 'value' or 'valueForKey:@"value"' on it just raises as exception, so I assumed bindings was doing something clever under the hood to know that the value binding for an NSTextField actually means stringValue;, or possibly it's using another method entirely which isn't called by setStringValue:.
This is an important lesson to understand. The names of bindings are different from the names of properties. Both bindings and properties are "attributes" of a class or object, but they are not the same thing. Sometimes they correspond, but they don't have to. A binding name is just something that can be used with the methods of the NSKeyValueBindingCreation and NSPlaceholders protocols. The class which adopts one of those protocols interprets the name however it wants.
On Jan 18, 2014, at 10:17 PM, Roland King wrote:
> So it would seem that only a user-initiated change of the value fires the binding in reverse (which is what the example binding code in the Apple docs suggests) but setting the stringValue into the thing directly, doesn't. That's a surprise, I expected that either way of setting the value would result in the binding firing. I can see from the stack trace that the binding code is triggered directly from the textFieldShouldEndEditing: method.
I suggest you take a look at the Cocoa Bindings Programming Topics: How Do Bindings Work? article <https://developer.apple.com/library/mac/documentation/cocoa/conceptual/CocoaBindings/Concepts/HowDoBindingsWork.html>.
The point is not that that's how bindings are _necessarily_ implemented for the Cocoa classes which support bindings. The point is to illustrate that how bindings behave is entirely up to the class which implements them. Also, consider what the -observeValueForKeyPath:… method shown ultimately does when it sees a model-initiated change. It sets its own "angle" property. If setting its property caused it to set the property on the model through the binding key path, it might get into an infinite loop.
On Jan 18, 2014, at 10:39 PM, Roland King wrote:
> On 19 Jan, 2014, at 12:25 pm, Quincey Morris <email@hidden> wrote:
>
>> Alternatively, don’t use ‘set<anything>’ on the text field at all, but let the Browse button action method update the *data model*, and then let the binding change the text field. This may seem weird, because the button action method would have to apply the reverse transformer itself, but it’s actually cleaner, because it doesn’t make the button behavior depend on the text field.
>
> That's what I eventually did. It's cleaner because the Browse button (and NSOpenPanel) produce an NSURL anyway, which I set right onto the model and that then updates the text field with the string value. What I was trying, and failing, to do before would have turned the URL into a String, set that on the text field and have the transformer turn it back into a URL again, which was contorted, but felt like it ought to have worked.
I think it's cleaner to have the Browse button affect the model (through the controller) than for it to affect another view. It's generally rare for one view to directly interact with another view. Each view should interact with the controller and the controller should be responsible for updating other views. Having views know about each other makes the user interface design fragile. It's hard for you to redesign the interface, swapping out types of views for other types of views or the like.
Surely, the Browse button triggers an action method on the controller, anyway. So, it's not the button that knows about the text field (I hope). The button just targets the controller. The controller should then update state, either internal to itself or in the model, and tell views to update themselves to reflect that state. This latter part is what bindings facilitate.
> The only bad bit about this is I had to make the model object a property of the view so the browse button could set the URL property on it, with bindings alone everything was in IB and the view object implementation had no knowledge of the model object at all, it just set a string path onto itself and the model was supposed to pick it up, except it didn't.
I'm not entirely sure what you're talking about here. If the text field is bound to the model (through the controller), then there's already a key path to the relevant part of the model. The controller could simply traverse the same path as is used for the model key path used in the binding. [I don't recommend using a key path string to do that, necessarily. It should be able to access the properties in the chain in the normal way (calling accessors, perhaps with dot syntax).]
NSViewController has a handy representedObject property to serve as the, well, object which the view represents. You can add a similar property to any subclass of NSWindowController that you create.
Regards,
Ken
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden