问题
I want to display some items in my sidebar, with the count of each tag also displayed:
How do I do this efficiently and automatically? The easy option would be to use cocoa bindings, but I'm not sure what the best way to do this: would each button needs it's own NSArrayController with a fetch predicate set for the 'tag'? That could end up with X number of NSArrayControllers (one for each tag) which would be pretty heavy-weight (I would think).
The other option is to create fetch requests manually, then refetch for every change in managed object context. But that seems a bit messy and not-as-automatic.
Is there a simpler solution for this? I've googled around and haven't found anything.
回答1:
Let's assume that in your NSOutlineView you have a childrenKeyPath of "children" and the children have a boolean isNew attribute. What you want is a nice numberOfNewItems
bubble in the cell view for one object class. I'll call that the parent object.
If you just want the number of objects in the childrenKeyPath, heck, that's even easier, but I'll take you through the more complex case of tracking a specific boolean property on the children because it's easy enough to simplify this pattern.
In the table cell view for the parent object add a recessed button with the title bound to objectValue.numberOfNewItems
and hidden bound to objectValue.NumberOfNewItems
with value transformer NSNegateBoolean. If you just want the number of children, swith those keypaths to objectValue.children.count
and you're done. If you want to track a property like isNew, let's continue...
In the parent object class, there is this code:
- (NSNumber*) numberOfNewItems
{
// Collection operator on boolean returns total number of new children
return [self valueForKeyPath:@"children.@sum.isNew"];
}
// This setter does nothing, but with KVO it causes bindings
// to numberOfNewItems to call the above getter
- (void) setNumberOfNewItems:(NSNumber*)number { }
// This ensures that changes to the children set causes KVO calls to the above getter
+ (NSSet*) keyPathsForValuesAffectingNumberOfNewItems
{
return [[NSSet alloc] initWithObjects:@"children", nil];
}
What that does is cause numberOfNewItems
to be recalculated any time the table cell's objectValue gets a new child added to or removed from its children
to-many relationship.
In the childItem class, there's this in the one place that the childItem is transitioned from isNew to not-New:
// If collapsing an item, mark it as not new
if (!isExpanded.boolValue && self.isNewValue) {
self.isNew = @NO;
[self.parent setNumberOfNewItems:nil]; // Triggers KVO for button binding
}
... and what that does is use the parent's empty setNumberOfNewItems setter to force the button binding to call the getter. So the whole to-many children
relationship is enumerated each time an item is marked not-new. I supposed that could be improved, but I haven't played around with that yet.
I took advantage of the fact that an item is marked not-new only one place in my code. If you have several things resetting or setting isNew in the child, you might override setIsNew in the childItem class to call self.parent setNumberOfNewItems:nil
instead.
The trick here is that having the parent add itself as a KVO observer for the isNew keypath for all children would be a terrible pain. So I wanted to avoid that. If you simply have the children call the empty setter in the parent, the parent can own the calculation, and there's no KVO outside what the bindings use.
Looks like this:
来源:https://stackoverflow.com/questions/24463979/itunes-like-count-buttons-using-cocoa-bindings