我有一个带复选框的NSOutlineView。我将复选框状态绑定到具有键shouldBeCopied的节点项。在节点项中,我有这样的getter和setter:
-(BOOL)shouldBeCopied {
if([[self parent] shouldBeCopied])
return YES;
return shouldBeCopied;
}
-(void)setShouldBeCopied:(BOOL)value {
shouldBeCopied = value;
if(value && [[self children] count] > 0)
[[self delegate] reloadData];
}
这里的想法是,如果父母被检查,孩子也应该。我遇到的问题是,当我检查父级时,如果它们已经扩展,它不会更新子视图。我可以理解它不应该被绑定更新,因为我实际上并没有改变它的值。但是reloadData应该不会导致绑定重新获取值,从而为子节点调用-shouldBeCopied
?我尝试过其他一些事情,例如-setNeedsDisplay
和-reloadItem:nil reloadChildren:YES
,但都没有效果。我注意到当我交换到xcode然后再返回时,显示器会刷新,这就是我想要的,所以我怎么能让它表现得那样呢?
答案 0 :(得分:1)
你的setter不会在更改之前和之后发送-willChangeValueForKey:和-didChangeValueForKey:因此绑定机制不会“注意到”这些更改。
另外,直接从模型对象告诉视图任何是......不是一个好方法。在这种情况下,由于您正在使用Bindings,您的树控制器应该记录更改(一旦您修复了setter以发送正确的通知)并更新了大纲视图。
答案 1 :(得分:1)
我有一个带复选框的NSOutlineView。我将复选框状态绑定到一个节点项,其中键为shouldBeCopied。
您是绑定列还是单元格?你应该绑定列。
-(BOOL)shouldBeCopied { if([[self parent] shouldBeCopied]) return YES; return shouldBeCopied; }
首先使用孩子的价值会不会更好?如果没有别的,它的检索成本更低(不需要消息)。
修改后的getter将如下所示:
- (BOOL) shouldBeCopied {
return (shouldBeCopied || [[self parent] shouldBeCopied]);
}
我遇到的问题是,当我检查父项时,如果它们已经扩展,则不会更新子视图。
有两种解决方案。一个更干净,将在10.5及更高版本上工作。另一个稍微脏了,适用于任何版本的Mac OS X.
脏解决方案是让setter方法的父代表代表其所有子代发布KVO通知。类似的东西:
[children performSelector:@selector(willChangeValueForKey:) withObject:@"shouldBeCopied"];
//Actually change the value here.
[children performSelector:@selector(didChangeValueForKey:) withObject:@"shouldBeCopied"];
这很脏,因为它有一个对象发布有关另一个对象属性的KVO通知。每个对象应该只声称知道自己属性的值;声称知道另一个对象的属性的值的对象可能存在错误,导致错误和/或低效的行为,更不用说代码引发头痛的倾向。
更干净的解决方案是让每个对象都观察其父对象的这个属性。在将自己添加为观察者时通过the NSKeyValueObservingOptionPrior
option,以便在更改之前收到通知(您将在the observation method中通过发送[self willChangeValueForKey:]
来回复)并在更改后收到通知(您将在观察方法中通过发送[self didChangeValueForKey:]
)来回复。
使用干净的解决方案,每个对象只发送有关其自身属性更改的KVO通知;没有对象发布有关其他对象属性的通知。
当孩子自己的属性值为YES
时,你可能会想让孩子不发送这些KVO通知,因为在这种情况下,父母的值无关紧要。但是,你不应该这样做,因为这只适用于儿童;如果一个较低的后代将属性设置为NO
,那么该对象的祖先的值毕竟是重要的,并且该对象只会在其每个祖先发布它时才会收到通知。