(ugh, reposted due to size moderation, again)
On Sep 11, 2015, at 13:40 , Alex Hall <[email protected]> wrote:
> What I've done so far is set things up, but not gotten the app to run. At
> first, I was getting some odd errors, but I eventually realized that
> populating my tweetsArray object in viewDidLoad was a bad idea, so now I do
> that in the property declaration:
> dynamic var tweetsArray:[Tweet]=[Tweet(text:"tweet 11"),
> Tweet(text:"tweet 12”)]
You’ve *still* got a conceptual problem, but this one is about KVC, not
bindings. KVC deals with properties which are values identified by *name* (key)
within classes. There are scalar (one-to-one) properties — simple values — and
collection (one-to-many) properties — multiple values which are isolated by
(say) a numeric index.
In theory, there can be an “array” property, but that would be a scalar
property whose single value happened to be a NSArray. Given the other details
of KVC’s machinery, trying to use such an array property is a nightmare you
don’t want to be stuck in. What you want, and what you really have, is an
indexed collection of tweets, and you need to stop thinking of the property as
an array — even though the backing store may have type [Tweet] and the KVC
machinery accesses the collection via the NSArray class-cluster API — but not
necessarily actual arrays (sometimes it uses proxy objects that refer to actual
arrays, but that’s another discussion). Indeed, you should call your property
“tweets”, not “tweetsArray”.
The internal magic of KVC is such that it provides a standardized mechanism of
translating between property names (for a given class) and methods (of the
class) the produce the actual values. Such methods are called accessors, and
the method (naming) requirements depend on the kind of properties.
For scalar properties, there are two accessor methods: a getter and a setter
(xxx and setXxx:, for a property named “xxx”). For indexed collection
properties, there are about 8 different accessor methods, but you usually
implement only a minimum required subset (of 2 accessors, one for inserting and
one for removing). All the rest of the accessors default to standardized
behavior and/or translate themselves into your custom accessors.
> As to my controller and bindings:
>
> array controller:
> * content array: view controller.tweetsArray (which again, is dynamic);
> controller key is empty
Correct, though as I said, you’re doing nothing but creating confusion by
calling this “tweetsArray” rather than “tweets”, in your data model.
> * Content Array For Multiple Selection: View Controller.tweetsArray (though I
> don't need this, as I've disable multiple selection in my table)
Don’t need. Leave it alone and let the binding provide default behavior.
> * Selection Indexes: controller to bind to is View Controller, but I don't
> know what to put for a key path here. This is a property of the array
> controller itself, not anything it needs to get anywhere else, so I don't
> quite know why this field is here. Unless it's observing itself?
Don’t need, probably. If your view controller wants to have it’s own “mirror”
of the selection that the array controller maintains, you can give it a
suitable property, bind it here, and it will reflect the selection through the
magic of bindings. There’s actually nothing interesting going on here, it’s
just a piece of array controller convenience functionality.
> * Managed Object Context: same as Selection Indexes. I'm not quite sure what
> to put for values here.
Don’t need. This is for Core Data, and absolutely nothing else.
> Table View:
> * Content: array controller.arrangedObjects
Correct.
> * Selection Indexes: Array Controller.selectionIndexes
> * Sort Descriptors: Array Controller.sortDescriptors
Don’t really need. IIRC table views establish these particular bindings by
default, if you don’t. You only ever set these manually if you’re bindings
targets are non-standard. But what you’ve done isn’t wrong.
> Cell text field: this one's odd, because I thought the controller was
> supposed to be TableCellViewController, but that's not in the popup list of
> available options.
> * Value: Array Controller.selection.displayText (recall that displayText is a
> string property of the Tweet objects of which tweetsArray is full)
Nope. A table cell is a placeholder, since it obviously has to be different at
every row. If you bind subviews to a particular target, all rows will show the
same thing, and that’s not what you want.
Generally, you bind a subview’s value to TableViewCell (which *should* be in
the popup), and use a model key of “objectValue.something”. In this case, it
sounds like it should be “objectValue.displayText”.
There’s a completely separate piece of table view machinery that ensures the
“objectValue”, for each table cell, contains a reference to the correct tweet
for its row. This can itself be a binding, a default table behavior, or it can
be done in a delegate method.
BTW, if you look at the table view documentation to find out how to supply this
piece, you’ll find a lack of information. It’ll tell you that binding a table
view’s content is an “advanced” topic, and give you no further help than a
comically inadequate example.
> Right now, I'm getting an error:
> [Bindings_Tests.ViewController count]: unrecognized selector sent to instance
> 0x6080000c3db0
>
> Clearly, I'm missing a key somewhere, because that should be [ViewController
> countOfTweetsArray]
Nope. It should be “count”. The bound object, whatever it was, asked for the
“count” of what it expected to be an indexed collection property. KVC takes
such a request, and translates it into an accessor internally, or resolves it
into default behavior. The latter is the correct outcome in this case. If it
used the accessor, the method *would have been* called ‘countOfTweetsArray’,
but the client — the bound UI element — doesn’t know or care. It just wants the
“count”.
The actual problem here is that you left out the *property* key. I don’t know
which binding this was, but it looks like you chose the view controller as
target, and omitted to specify “tweetsArray” as the model key. (Or, due to one
of the above problems, the entire binding should have been to a different
target.)
> , which I *did* implement, just in case it would help:
> private func countOfTweetsArray() -> Int {
> return tweetsArray.count
> }
>
> I've also added:
> private func objectInTweetsArrayAtIndex(index:Int) -> Tweet{
> return tweetsArray[index]
> }
Don’t bother with these. They *are* the correct accessors for read access to an
indexed one-to-one property called “tweetsArray”, but due to a horrible
long-standing defect in KVC, it’s not really practical to use them (in many
cases), and they won’t be used in this case, and you don’t need them in this
case, simply because you have a property called “tweetsArray” which does their
duty.
_______________________________________________
Cocoa-dev mailing list ([email protected])
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 [email protected]