2015-09-04 46 views
2

我想使用NSArrayControllerNSTableView来允许多选,但只有在选择单个对象时才提供选定的对象(并且当选择一个或多个对象时为nil) 。为什么我的KVO依赖项在NSArrayController中不起作用

我已经尝试与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)。为什么是这样?

回答

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警告)。

+0

在'-awakeFromNib'中添加自我观察看起来并不完全可靠。为什么不重写'-initWithContent:'?在'-observeValueForKeyPath:...'中,如果上下文是你的,不要通过超级调用。使'kObservingSelectionIndexesContext''NSString * const'。在基础属性已经改变之后执行'willChange ...'是不安全/可靠的。使用'NSKeyValueObservingOptionPrior',并根据“change”是否包含“NSKeyValueChangeNotificationIsPriorKey”调用'willChange ...'或'didChange ...'。这就是它的目的。 –

+0

是否保证'initWithContent'始终被调用?如果它是用'initWithCoder:'初始化的呢?此外,它看起来像NSKeyValueObservingOptionPrior [不适用于NSArrayController](https://lists.apple.com/archives/Cocoa-dev/2010/Apr/msg00092.html)([radar](https:// openradar。 appspot.com/7834918))。 – DanielGibbs

+0

'-initWithContent:'是'NSObjectController'的指定初始值,'NSArrayController'从其继承。 'NSArrayController'不声明一个新的指定初始化器。所有其他初始化器都必须通过指定的初始化器以良好行为的类进行路由。所以,是的,它确保了'NSArrayController'或'NSObjectController'中的模块错误,正如我们所看到的那样,它不是不可能的。 –

1

属性NSArrayController是奇怪的巫术。我不知道是否键值观察它(而不是一个从它的路径),当选择改变时产生改变通知。毕竟,它会返回一个代理,并且没有理由相信该代理的身份会随着时间而改变。

在任何情况下,您的实际selectedObject方法实际上并不使用selection(它不应该)。它使用arrangedObjectsselectionIndexes。所以,你应该返回一组包含那些密钥从+keyPathsForValuesAffectingSelectedObject。当然,如果你使用的是基于视图的表,你需要确保表视图的selectionIndexes绑定绑定到数组控制器的selectionIndexes属性,或者数组控制器不知道任何关于选择的内容在表格视图中。 (对于基于单元格的表格视图,您通常会将列绑定到数组控制器,并且表视图会自动绑定基于列绑定的绑定。)

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

+0

嗨,肯,谢谢你的回复。我更新了'+ keyPathsForValuesAffectingSelectedObject'来返回'[@“selectionIndexes”,@“selectionIndex”,@“arrangedObjects”],但这并没有改变任何东西。表视图的'selectionIndexes'被正确绑定到'NSArrayController'的'selectionIndexes'。感谢关于名称的提示,这是一个很好的观点。 – DanielGibbs

+0

如果您的'selectedObject'的观察者也观察到'selectionIndexes'和'selectionIndex',它是否会得到这些改变的通知? –

+0

是的,它的确如此。看来它是使用'keyPathsForValuesAffecting ...'指定的依赖项,它们不能与'NSArrayController'一起使用。创建我自己的子类并手动观察值的作品。 – DanielGibbs

相关问题