Re: Binding to currently selected object in a list?
On Oct 9, 2009, at 5:08 PM, A B wrote: Is there a way to bind an ivar to the currently selected object of a list which is being represented in an NSTableView via bindings? Unfortunately there does not seem to be a selectedObject key (though there is one for selectedObjects). Binding to the controller's selection doesn't really work as I'd expect and even if it did, I'd only be getting back a proxy object - which negates the purpose for which I want to use it, which is to call methods on the object. I had put in a hack which observes changes in selection, gets the selectedObjects list, checks for a count 0, grabs the first and stuffs it into the ivar, etc. but not only does that feel really hackish, but doesn't always work. This would seem to be a pretty simple thing to want to do (i.e. present a list of objects, do things to/with the currently selected object) but I'm at somewhat of a loss to understand what I'm doing wrong. There's something just like this for NSPopUpButton. You bind its 'content' (and possibly 'contentObjects') binding to an indexed collection property of the controller. Then you bind its 'selectedObject' binding to a to-one property of the controller. When the pop-up is first set up, it displays the selected object as originally set on the controller's to-one property. Then, as the user selects items from the pop-up, the newly selected object is passed to the setter for that to-one property. Strangely, there's nothing quite like that for NSArrayController. I think the best you can do is to bind the array controller's selectionIndexes binding to an attribute of your coordinating controller. (For illustration purposes, I'll call this attribute property selectedWidgetIndexes.) Then that coordinating controller can have a selectedWidget to-one property which is set up to reflect the object which is selected in the array controller. One way is to make the selectedWidget property computed-on-the-fly based on the selection indexes: +(NSSet*)keyPathsForValuesAffectingSelectedWidget { return [NSSet setWithObject:@selectedWidgetIndexes]; } -(Widget*)selectedWidget { if (![self.selectedWidgetIndexes count]) return nil; return [self.widgets objectAtIndex:[self.selectedWidgetIndexes firstIndex]]; } In this scenario, you don't actually have an ivar backing the selectedWidget property. It's completely based off of the selectedWidgetIndexes property and the widgets to-many property. (The widgets property is assumed to be the indexed collection property that the array controller is representing. The selectedWidgetIndexes property can be a typical @synthesized copy property.) Another approach is to implement the setter for selectedWidgetIndexes and use that to update the selectedWidget property directly. In this case, selectedWidget isn't computed on the fly, but is actually held (cached) in an ivar: -(void)setSelectedWidgetIndexes(NSIndexSet*)newSelectedWidgetIndexes { if (![selectedWidgetIndexes isEqual:newSelectedWidgetIndexes]) { [selectedWidgetIndexes release]; selectedWidgetIndexes = [newSelectedWidgetIndexes copy]; if ([selectedWidgetIndexes count]) self.selectedWidget = [self.widgets objectAtIndex: [selectedWidgetIndexes firstIndex]]; else self.selectedWidget = nil; } } You don't need +keyPathsForValuesAffectingSelectedWidget since you're going through the setter for the selectedWidget property. The selectedWidget property can be a typical @synthesized retain property. By the way, notice that all of this discussion is in terms of the array controller and the coordinating controller. No mention of the table view because it's not directly relevant. The MVC design pattern in action. ;) 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: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Binding to currently selected object in a list?
Ken, Thank you for the well-expressed response. I understand both options as you've explained them and will try them both to see which ones I end up having a better feel for. I may have to go for the second example as I think that it will get executed in a more deterministic order earlier - which is something that I will need to be reasonably assured of as the 'selectedWidget' value is one that is used by other KVO-triggered actions and that therefore lack of guaranteed order of setting can end up causing me problems. On Friday, October 09, 2009, at 11:57PM, Ken Thomases k...@codeweavers.com wrote: On Oct 9, 2009, at 5:08 PM, A B wrote: Is there a way to bind an ivar to the currently selected object of a list which is being represented in an NSTableView via bindings? Unfortunately there does not seem to be a selectedObject key (though there is one for selectedObjects). Binding to the controller's selection doesn't really work as I'd expect and even if it did, I'd only be getting back a proxy object - which negates the purpose for which I want to use it, which is to call methods on the object. I had put in a hack which observes changes in selection, gets the selectedObjects list, checks for a count 0, grabs the first and stuffs it into the ivar, etc. but not only does that feel really hackish, but doesn't always work. This would seem to be a pretty simple thing to want to do (i.e. present a list of objects, do things to/with the currently selected object) but I'm at somewhat of a loss to understand what I'm doing wrong. There's something just like this for NSPopUpButton. You bind its 'content' (and possibly 'contentObjects') binding to an indexed collection property of the controller. Then you bind its 'selectedObject' binding to a to-one property of the controller. When the pop-up is first set up, it displays the selected object as originally set on the controller's to-one property. Then, as the user selects items from the pop-up, the newly selected object is passed to the setter for that to-one property. Strangely, there's nothing quite like that for NSArrayController. I think the best you can do is to bind the array controller's selectionIndexes binding to an attribute of your coordinating controller. (For illustration purposes, I'll call this attribute property selectedWidgetIndexes.) Then that coordinating controller can have a selectedWidget to-one property which is set up to reflect the object which is selected in the array controller. One way is to make the selectedWidget property computed-on-the-fly based on the selection indexes: +(NSSet*)keyPathsForValuesAffectingSelectedWidget { return [NSSet setWithObject:@selectedWidgetIndexes]; } -(Widget*)selectedWidget { if (![self.selectedWidgetIndexes count]) return nil; return [self.widgets objectAtIndex:[self.selectedWidgetIndexes firstIndex]]; } In this scenario, you don't actually have an ivar backing the selectedWidget property. It's completely based off of the selectedWidgetIndexes property and the widgets to-many property. (The widgets property is assumed to be the indexed collection property that the array controller is representing. The selectedWidgetIndexes property can be a typical @synthesized copy property.) Another approach is to implement the setter for selectedWidgetIndexes and use that to update the selectedWidget property directly. In this case, selectedWidget isn't computed on the fly, but is actually held (cached) in an ivar: -(void)setSelectedWidgetIndexes(NSIndexSet*)newSelectedWidgetIndexes { if (![selectedWidgetIndexes isEqual:newSelectedWidgetIndexes]) { [selectedWidgetIndexes release]; selectedWidgetIndexes = [newSelectedWidgetIndexes copy]; if ([selectedWidgetIndexes count]) self.selectedWidget = [self.widgets objectAtIndex: [selectedWidgetIndexes firstIndex]]; else self.selectedWidget = nil; } } You don't need +keyPathsForValuesAffectingSelectedWidget since you're going through the setter for the selectedWidget property. The selectedWidget property can be a typical @synthesized retain property. By the way, notice that all of this discussion is in terms of the array controller and the coordinating controller. No mention of the table view because it's not directly relevant. The MVC design pattern in action. ;) 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: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Binding to currently selected object in a list?
Is there a way to bind an ivar to the currently selected object of a list which is being represented in an NSTableView via bindings? Unfortunately there does not seem to be a selectedObject key (though there is one for selectedObjects). Binding to the controller's selection doesn't really work as I'd expect and even if it did, I'd only be getting back a proxy object - which negates the purpose for which I want to use it, which is to call methods on the object. I had put in a hack which observes changes in selection, gets the selectedObjects list, checks for a count 0, grabs the first and stuffs it into the ivar, etc. but not only does that feel really hackish, but doesn't always work. This would seem to be a pretty simple thing to want to do (i.e. present a list of objects, do things to/with the currently selected object) but I'm at somewhat of a loss to understand what I'm doing wrong. ___ 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