为什么我的 KVO 依赖项在 NSArrayController 中不起作用
Why doesn't my KVO dependency not work in NSArrayController
我想将 NSArrayController
与 NSTableView
结合使用以允许进行多项选择,但仅在选择单个对象时提供选定对象(而 nil
当 none 或多个被选中)。
我已尝试使用 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
)。这是为什么?
NSArrayController
的selection
属性是奇怪的巫术。我不知道观察它的键值对(而不是 通过 的路径)是否会在选择更改时产生更改通知。毕竟,它 return 是一个代理,没有理由相信该代理的身份会随着时间而改变。
无论如何,您实际的 selectedObject
方法实际上并没有使用 selection
(也不应该)。它使用 arrangedObjects
和 selectionIndexes
。因此,您应该 return 一个包含 来自 +keyPathsForValuesAffectingSelectedObject
.
的那些 键的集合
当然,如果您使用基于视图的 table,您需要确保 table 视图的 selectionIndexes
绑定绑定到数组控制器的 selectionIndexes
属性,否则数组控制器将不知道 table 视图中的选择。 (对于基于单元格的 table 视图,您通常会将列绑定到数组控制器,并且 table 视图会根据列的绑定自动绑定自己的绑定。)
最后,我认为您应该为 selectedObject
选择一个不同的名称。 Apple 很可能有一个该名称的私有方法,或者将来会添加一个。
我设法通过创建 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) to avoid removing any observers the superclass may have in dealloc
(as cautioned against here)。
我想将 NSArrayController
与 NSTableView
结合使用以允许进行多项选择,但仅在选择单个对象时提供选定对象(而 nil
当 none 或多个被选中)。
我已尝试使用 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
)。这是为什么?
NSArrayController
的selection
属性是奇怪的巫术。我不知道观察它的键值对(而不是 通过 的路径)是否会在选择更改时产生更改通知。毕竟,它 return 是一个代理,没有理由相信该代理的身份会随着时间而改变。
无论如何,您实际的 selectedObject
方法实际上并没有使用 selection
(也不应该)。它使用 arrangedObjects
和 selectionIndexes
。因此,您应该 return 一个包含 来自 +keyPathsForValuesAffectingSelectedObject
.
当然,如果您使用基于视图的 table,您需要确保 table 视图的 selectionIndexes
绑定绑定到数组控制器的 selectionIndexes
属性,否则数组控制器将不知道 table 视图中的选择。 (对于基于单元格的 table 视图,您通常会将列绑定到数组控制器,并且 table 视图会根据列的绑定自动绑定自己的绑定。)
最后,我认为您应该为 selectedObject
选择一个不同的名称。 Apple 很可能有一个该名称的私有方法,或者将来会添加一个。
我设法通过创建 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) to avoid removing any observers the superclass may have in dealloc
(as cautioned against here)。