On Sep 8, 2015, at 11:04 PM, Alex Hall <mehg...@icloud.com> wrote:

> I've created a new thread, because I obviously don't understand nearly as 
> much about Cocoa Bindings as I thought I did.

Bindings is a complex and advanced topic.  It's built on top of other 
technologies, which you need to understand and be comfortable with first: 
Key-Value Coding and Key-Value Observing.  You should start with Key-Value 
Coding and work with that until you're comfortable.  Then work to understand 
Key-Value Observing.  And only then move on to bindings.


> I think my biggest problem is what values/keys to enter where in the Bindings 
> Inspector for my views and controller. I get that selection, selectedIndexes, 
> objectValue, and so on are all properties of the controller, but when do you 
> use them, and what others are commonly needed/where are they needed? When do 
> you use a property name stored in the controller, versus one stored in your 
> model, versus one stored in your view controller? When do you simply leave a 
> path blank, versus needing to provide a value? Why is the "controller key" 
> field sometimes unavailable, and sometimes not?

Some of this is just experience and familiarity with the frameworks.  But a lot 
of it is not about some pat rules you follow.  The answer is often "you enter 
the paths that lead to the information you want/need for your design/purpose".

Rather than asking a general question like this, ask about one specific case 
that actually confuses you.  Keep asking for clarification of specific bits 
about that one case until you understand.  Then move on to the next case, etc.

If you're not already aware of it, there's the Cocoa Bindings Reference 
document.  It documents what bindings the Cocoa classes support and explains 
them a bit.  There's also a section describing binding types and options.  
Unfortunately, that document is not always as clear or complete as we might 
like, but it can be helpful.  It's a reference, though; don't read it through.  
Just use it look specific stuff up when the needs arises.
<https://developer.apple.com/library/mac/documentation/Cocoa/Reference/CocoaBindingsRef/CocoaBindingsRef.html>


> Speaking of keys, values, and paths, in a recent email, Ken said:
>> Basically, these keyPathsForValuesAffecting<Key> methods should list the key 
>> paths of any properties used in the implementation of the computed property 
>> getter.  They are the "inputs" to the computation of the computed property.
> 
> But why are they needed? Is this method taking the place of something I might 
> do in IB, or is this something different? Why would anything other than my 
> view controller (where this method is implemented) care about *how* a 
> computed property is computed? If I'm standing between Alice and Bob, and I 
> can hear both of them but they're too far apart to hear each other, I'm the 
> controller. Bob asks me to ask Alice her hame, so I do. What this computed 
> property situation feels like is me then asking Alice for the story behind 
> her name, information totally unrelated to the specific detail Bob needs, and 
> not in any way affecting my ability to get the name or to tell Bob what it is.

Your example with Alice and Bob is passive.  It uses a "pull" model.  When Bob 
wants to know Alice's name he asks.  But there are plenty of places in app 
programming that need an active approach, a "push" model.  Bob asked Alice her 
name once in the past and painted it on a billboard.  From then on, he sits 
idle, figuring his work is done.  If and when Alice changes her name, and 
assuming Bob wants his billboard to have current information, there needs to be 
an active agent that informs Bob that Alice's name may have changed and he 
needs to ask for it again.  And that active agent needs some way to learn that 
Alice has changed her name so he can do this informing of Bob.

This is the role of Key-Value Observing.  So, you need to understand that and 
what it does.

For example, consider a Rectangle class with "width" and "height" as stored 
properties and "area" as a computed property.  The area is the product of width 
times height so, of course, it changes when either width or height changes.

Some code asks the framework to inform object O when property "width" of some 
Rectangle object R changes.  The class of object O has implemented a method to 
receive change notifications.  It can do whatever it wants in response to such 
change notifications.

"Width" is a stored property of Rectangle.  It only changes when something 
sends a message to R.  (Assigning to the property is actually a method 
invocation under the hood, at least for dynamic properties.)  The KVO machinery 
knows the Cocoa naming conventions and, when the observer is added, it hooks 
into all of the methods on R whose names match the naming conventions for 
methods which might change its width.  (In particular, the -setWidth: method.  
That's Objective-C syntax, but a method of the same name exists under the hood 
for a Swift class.)  When one of those methods is called on R, KVO sends change 
notifications to all of the observers that have been added to R for the width 
property.


Now consider: some code asks the framework to inform object O when property 
"area" of some Rectangle object R changes.

"Area" is a computed property; it doesn't "have" a persistent value, it can 
_produce_ a value on demand.  It's basically just a method that returns a 
value.  The code that computes that value is arbitrary and opaque to KVO.  
(*We* know that it consists of "return width * height", but there's no way for 
the KVO machinery to know that.)  In particular, there's no call to a method 
whose name matches the naming conventions for one which might change the value 
of area.  Nothing calls -setArea:.  The area property will just "spontaneously" 
be different the next time something queries it after either width or height 
changes.

Now, how can KVO notify observers when such a computed property changes?  One 
horrible(!) approach would be polling.  It could query the value over and over 
and, if it changes, it can send change notifications.  You'll be glad to hear 
that that's not how it works.

Another approach would be to force you, the implementer, to manually generate 
change notifications.  You could surround every place where you modify width or 
height with willChangeValueForKey()/didChangeValueForKey() calls:

    self.willChangeValueForKey("area")
    width = someNewValue
    self.didChangeValueForKey("area")

And the same for the height property.

You can use property observers to accomplish this somewhat more automatically:

    dynamic var width: Double = 0 {
        willSet { self.willChangeValueForKey("area") }
        didSet { self.didChangeValueForKey("area") }
    }
    dynamic var height: Double = 0 {
        willSet { self.willChangeValueForKey("area") }
        didSet { self.didChangeValueForKey("area") }
    }

Of course, that's extremely tedious and error prone.

Luckily, there's a better way.  This is what the 
keyPathsForValuesAffecting<Key> method is for.

When something observes R for changes of its area property, KVO calls 
Rectangle.keyPathsForValuesAffectingValueForKey("area").  The default 
implementation of that method takes the key name ("area") and combines it with 
"keyPathsForValuesAffecting" to construct the method name 
keyPathsForValuesAffectingArea.  It then checks if the Rectangle class responds 
to that method and, if so, calls Rectangle.keyPathsForValuesAffectingArea() to 
get a set of key paths.  Now it adds its own internal observers of R for 
changes in any of the properties named by the key paths returned by that 
method.  When any of those properties changes, it also emits change 
notifications for the area property.

Although you probably hate this system by now, it's really quite elegant.  One 
of the nice things is that the specification of the things which result in a 
change of area is defined in one place, which can be put right next to the 
definition of the area property itself.  They can be seen together and changed 
together.  Contrast this with the approach of manually calling will/didChange… 
where tidbits about the area property were spread around to all of the other 
properties which affect area's value.

Also, this makes the mechanism extensible.  Extensions can add computed 
properties to a class based on its existing properties and make them 
KVO-compliant without having to muck about hooking into the existing properties.


> I'm currently binding my ArrayController to my ViewController. In the sample 
> from Apple, though, they bound theirs to an ImageController class, if I 
> recall the description of the figures correctly. Why did they do that instead 
> of binding to a view controller, since they want to control a view? How did 
> they get that controller to appear in the Bindings Inspector at all? Link:
> https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaBindings/Concepts/HowDoBindingsWork.html

"GraphicController" in that document seems to just be the name assigned to an 
instance of NSObjectController in the NIB.  You can assign your own labels to 
objects in Interface Builder and they'll show up in the pop-ups, etc.  They 
also seem to be using GraphicController as the name of a variable in the 
associated code snippets.  Presumably it's an outlet connected to the 
NSObjectController in the NIB.

Also, they bound a view — a view which implements a custom binding — to that 
controller.  So, it's not quite analogous to you binding your array controller 
to your view controller.  (I mean, it's not entirely dissimilar, either, but 
your question focuses on one of the differences.)

Unfortunately, this document is a little outdated in that it shows the custom 
"angle" binding of that joystick view class being established in Interface 
Builder, as well as code.  That used to be possible using IB plug-ins, but it 
no longer is.  Custom bindings can only be set up in code.


> My current project tries to use a model to adjust the display of a table, 
> through an ArrayController and ViewController. Obviously, this is too complex 
> a starting task for me. What do you recommend for a simpler app, so I can 
> have a better chance of getting it working and seeing how it's supposed to 
> work? I thought of something like making one edit field mimic another, so as 
> I type into either one, the other updates. There's a whole edit protocol, 
> though, that I'm not sure will help; I don't want it to introduce a new layer 
> of complexity, I just want something simple to begin with.

You can do that.  You can ignore the NSEditor and NSEditorRegistration 
protocols for now.  For the text fields, though, you need to pay attention to 
the binding options and when the text field sends its view-initiated update of 
its value to the model.  Hint: experiment with the Continuously Updates Value 
option.

There's a couple of examples in the Cocoa Bindings Programming Topics, one for 
connecting a slider with a text field and a more complex one with an attacker 
and a weapon choice.

However, to keep you motivated may I suggest an example that's just a 
simplified version of your app.  Make an app with a single array of Tweets.  
So, no dictionary mapping names to arrays.  Don't bother with the computed 
property to pick the current array out of that dictionary.  Just one array 
property of your view controller.

Start with the array having hard-coded contents.  Five Tweets.  Keep the Tweet 
class simple so it just has "from" and "text" properties or something like that.

Then, just get that array to show in a table view using bindings.  Follow the 
Populating a Table View Using Cocoa Bindings article of the Table View 
Programming Guide for Mac.
<https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TableView/PopulatingViewTablesWithBindings/PopulatingView-TablesWithBindings.html>

Next, add an "Add a Tweet" button to the view and hook it up to an action 
method which adds a new Tweet to the array in a KVO-compliant manner.  If you 
get it right, the table view will reflect the new Tweet.


> In his most recent email, Ken said:
>> So, the array controller to serve the table view will be bound to some 
>> coordinating controller (likely File's Owner in a window or view NIB, 
>> AppDelegate in the MainMenu NIB).  If that's the TweetDataController, 
>> directly, then the model key path would be "currentTweetStream.tweets".
> 
> Where did Files Owner or App Delegate enter the picture? I've been using my 
> view controller so far, and it and shared user defaults are the only two I 
> have when I look at the bindings inspector for my ArrayController. That is, I 
> couldn't select my app delegate or anything else even if I wanted to, and I 
> don't see how they fit in.

This may be because I think in terms of NIBs and maybe you're using a 
storyboard.  (You kids and your newfangled gizmos! ;)  You should focus on the 
"some coordinating controller" part of what I said.  My parenthetical was just 
mentioning examples of common ways the appropriate coordinating controller 
could be represented _in a NIB_.  In a storyboard, the appropriate coordinating 
controller would be your view controller.

Regards,
Ken


_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to