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

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

Reply via email to