为什么我的KVO依赖不能在NSArrayController中工作

时间:2015-09-04 10:50:27

标签: cocoa key-value-observing nsarraycontroller

我想使用带有NSTableView的{​​{1}}来允许多项选择,但仅在选择单个对象时提供所选对象(如果未选择任何一个或多个,则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方法。这是为什么?

2 个答案:

答案 0 :(得分:1)

selection的{​​{1}}属性是奇怪的伏都教。我不知道键值是否观察它(而不是通过通过它的路径)在选择发生变化时会产生更改通知。毕竟,它返回一个代理,没有理由相信该代理的身份会随着时间而变化。

在任何情况下,您的实际NSArrayController方法实际上并未使用selectedObject(它不应该使用)。它使用selectionarrangedObjects。因此,您应该从selectionIndexes返回包含那些键的集合。

当然,如果您使用的是基于视图的表,则需要确保表视图的+keyPathsForValuesAffectingSelectedObject绑定绑定到数组控制器的selectionIndexes属性,或者只是赢得了数组控制器在表格视图中对选择一无所知。 (对于基于单元格的表视图,通常将列绑定到数组控制器,表视图将根据列的绑定自动绑定其自己的绑定。)

最后,我认为你应该为selectionIndexes选择一个不同的名称。 Apple有可能拥有该名称的私有方法,或者将来会添加一个。

答案 1 :(得分:0)

我设法通过创建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

我使用了一个上下文(根据this article)来避免移除超类在dealloc中可能拥有的任何观察者(正如here所提醒的那样)。