为什么我的 KVO 依赖项在 NSArrayController 中不起作用



我想使用NSArrayControllerNSTableView来允许多次选择,但仅在选择单个对象时提供所选对象(当没有或多个被选择时提供nil)。

我尝试用NSArrayController上的一个类别来实现这一点,如下所示:

@implementation NSArrayController (SelectedObject)
+ (NSSet *)keyPathsForValuesAffectingSelectedObject {
    return [NSSet setWithObject:@"selection"];
}
- (id)selectedObject {
    // Get the actual selected object (or nil) instead of a proxy.
    if (self.selectionIndexes.count == 1) {
        return [self arrangedObjects][self.selectionIndex];
    }
    return nil;
}
@end

由于某种原因,当数组控制器的选择改变时,selectedObject方法没有被调用(并且其他东西正在观察selectedObject)。为什么会这样?

NSArrayControllerselection属性是奇怪的巫术。我不知道key-value观察它(而不是通过 it的路径)是否会在选择更改时产生更改通知。毕竟,它返回一个代理,并且没有理由相信该代理的身份会随着时间的推移而改变。

在任何情况下,实际的selectedObject方法实际上并不使用selection(它不应该)。它使用arrangedObjectsselectionIndexes。因此,您应该返回一个包含+keyPathsForValuesAffectingSelectedObject中键的集合。

当然,如果你使用基于视图的表,你需要确保表视图的selectionIndexes绑定绑定到数组控制器的selectionIndexes属性,否则数组控制器将不知道任何关于表视图中的选择。(对于基于单元格的表视图,您通常会将列绑定到数组控制器,并且表视图将根据列的绑定自动绑定自己的绑定。)

最后,我认为你应该为selectedObject选择一个不同的名字。苹果很有可能已经有了这个名字的私有方法,或者将来会添加一个。

我设法通过创建NSArrayController的子类并手动观察selectionIndexes键来实现此工作。我更喜欢使用类别,但这似乎确实有效。

static NSString *const kObservingSelectionIndexesContext = @"ObservingSelectionIndexesContext";
@implementation BetterArrayController
- (void)awakeFromNib {
    [super awakeFromNib];
    [self addObserver:self forKeyPath:@"selectionIndexes" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:(void *)&kObservingSelectionIndexesContext];
}
- (void)dealloc {
    [self removeObserver:self forKeyPath:@"selectionIndexes" context:(void *)&kObservingSelectionIndexesContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (context == (void *)&kObservingSelectionIndexesContext) {
        [self willChangeValueForKey:@"selectedObject"];
        [self didChangeValueForKey:@"selectedObject"];
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}
- (id)selectedObject {
    // Get the actual selected object (or nil) instead of a proxy.
    if (self.selectionIndexes.count == 1) {
        return [self arrangedObjects][self.selectionIndex];
    }
    return nil;
}
@end

我使用上下文(根据本文)来避免删除dealloc中超类可能具有的任何观察者(此处警告)。

相关内容

  • 没有找到相关文章

最新更新