Re: Observing edits make to a table using bindings
Re: Observing edits make to a table using bindings
- Subject: Re: Observing edits make to a table using bindings
- From: Ken Thomases <email@hidden>
- Date: Wed, 21 Jan 2009 01:51:10 -0600
On Jan 20, 2009, at 5:18 PM, Randall Meadows wrote:
I have an NSTableView, whose content is supplied through bindings to
an array controller. Column A is supplied via
FieldListController.arrangedObjects.name, Column B via
FieldListController.arrangedObjects.value; column B is editable,
column A is not.
I'm trying to observe when a cell in Column B is edited
First, let me ask: from where are you observing? If you're writing
code in the model, then inside the property setter may be the proper
place to do whatever work should happen in response to a change. If
you're writing code in the controller, you might consider using target-
action from the table column cell to the controller.
Please keep in mind that KVO is for being informed of changes to
properties, not user actions. The former may result from more things
than just the latter.
by
[thing addObserver:self
forKeyPath:@"fieldList"
options:(NSKeyValueObservingOptionOld |
NSKeyValueObservingOptionNew)
context:nil];
When I edit a field (by double-clicking it) and commit the edit
(pressing Return), in my -
observeValueForKeyPath:ofObject:change:context: method, "keyPath" is
"fieldList" and "object" is the object that owns the fieldList that
is being observed.
The fieldList property of the "thing" object is, I presume, a to-many
relationship. That -- the relationship -- is what's represented by
the property. The objects at the other end of the relationship are
not part of the property. So, when you observe "fieldList" you are
asking to be informed when "thing" is newly related to some object, or
ceases to be related to some object, or when the ordering of the
objects in the relationship changes. You are _not_ asking to be
informed when properties of those related objects change, even if
that's what you were intending.
It's not clear what might be changing the relationship or why.
Editing the "value" of an object related through the fieldList
relationship should not require that anything change the fieldList
relationship at all. Checking the stack backtrace for a call to your
observeValueForKeyPath:... method might be illuminating.
I tried changing the observed keyPath to @"fieldList.value", but
that only succeeded in having the table not display anything at all
(which doesn't make sense to me either).
If you want to know when the "value" property of one of the related
objects changes, then you do have to observe the key path
"fieldList.value". Since that was breaking your program's behavior, I
suspect you were getting an exception but that the AppKit was
swallowing it in an inconspicuous manner. You need to check the
console output of your program (the Xcode console, if running from
Xcode, or the general console log if running from the Finder or Dock)
to see what the exception is. It will probably be telling you that
some property is not KVO-compliant.
The NSKeyValueChangeKindKey in the change dictionary is
NSKeyValueChangeSetting;
This is indicative of a problem. If a to-many relationship is fully
KVO-compliant, you would typically get one of
NSKeyValueChangeInsertion, NSKeyValueChangeRemoval, or
NSKeyValueChangeReplacement rather than NSKeyValueChangeSetting. The
first three indicate that the to-many relationship was modified in
terms of elements (some elements inserted, removed, or replaced). The
last indicates the entire relationship was replaced in whole.
Generally, KVC and Bindings will prefer the former to the latter, if
the design of the properties permits it.
NSKeyValueChangeNewKey and NSKeyValueChangeOldKey are both arrays
containing all the observed fields in "fieldList". But those
"fields" are not the values that are being edited, they are the
objects which contain the values that are being edited (i.e., [field
value] is what's getting edited (and [field name] is what's
displayed in Column A).
This is a consequence of the two things I've already mentioned.
Observing "fieldList" is only asking to be informed when the
relationships among objects is being changed. And, because fieldList
isn't fully KVO-compliant, something is being forced to wholesale
replace the relationship instead of being able to tweak it, and that
wholesale replacement affects how you're notified.
OK, so all that makes sense, in that I'm observing "fieldList", and
the notification hands me an array (which is fieldList). However,
it doesn't tell me *which* field in the array was edited; I have to
iterate over all the fields, comparing the values from "old" and
"new" to figure out which specific field was edited.
Is it possible to know exactly which array element was edited,
without registering observers on the entire contents of the array?
I figure it has something to do with the keyPath, but the exact
something has thus far eluded me.
If you observe fieldList.value, and if all involved properties are
properly KVO-compliant, then you should get a notification of kind
NSKeyValueChangeReplacement with NSKeyValueChangeIndexesKey holding
the indexes of the elements which have been replaced and
NSKeyValueChangeNewKey holding the new values. (You might get, and
should be prepared to get, notifications of other kinds, if the KVC
machinery decides on a different means of implementing the change
(e.g. removal followed by insertion at the same index), but
replacement seems the most likely for an edit to the value of a single
object in the relationship.)
Remember that referencing the key path fieldList.value yields an ad-
hoc array synthesized by iterating over the fieldList relationship and
asking each related object for its "value" property's value. Thus,
observing this key path for changes results in array-style change
notifications.
Cheers,
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