一些想法:
首先,你不应该同时实现 NSTableViewDataSource 协议和使用绑定——它通常是一个或另一个。如果您有特定的理由这样做,我会首先使用绑定让您的应用程序启动并运行,然后一步一步地从 NSTableViewDataSource 中添加您想要的任何功能,以确保一切正常。有一些绑定可用于支持工具提示。
接下来,我并不是说这是唯一的方法,但我建议将 ArrayController 放回 xib。NSController 子类和绑定到它们的控件之间似乎存在一种特殊的关系——绑定检查器中的 Controller Key: 条目强烈暗示了这一点,因为当您不使用它时它被禁用。我不确定,但我猜当您通过那个大 keyPath 绑定以返回到文档以获取 arrayController 时,这种魔法不会发生。
我还想知道为什么您希望 NSArrayController 位于控件绑定到它的窗口之外的其他地方?以及为什么你会让 WindowController与 Document共享NSArrayController?
NSArrayControllers 保持选择状态,因此每个窗口都有一个,或者更抽象地说,它们将靠近 UI,因此应该存在于每个需要一个的 nib 中。也就是说,除非您尝试做一些非常规的事情,例如在多个窗口之间共享单个选择状态(即更改窗口 A 中的选择,并且窗口 B 中的相应控件也会更改选择以匹配窗口 A)。我将在下面探讨这个问题,但简而言之,我想不出任何其他原因,您希望共享一个 arrayController 而不是将多个 arrayControllers 绑定到相同的基础数据。
如果选择共享是您的目标,我认为您最好做一些事情,例如让文档在每个窗口中的 nib 创建的 ArrayControllers 的 selectionIndexes 上设置键值观察,并让它们将选择传播到其他窗口' 数组控制器。
我把它编码了;它似乎工作。我从 Xcode 中基于标准 NSDocument 的 Cocoa 应用程序模板开始,并dataModel
在文档中添加了一个属性,并用一些数据伪造了它。然后我在 期间制作了两个窗口makeWindowControllers
,然后我添加了观察等,以使它们的选择相互跟随。一切似乎都很好地融合在一起。
压缩成一个代码块:
#import <Cocoa/Cocoa.h>
@interface SODocument : NSDocument
@property (retain) id dataModel;
@end
@interface SOWindowController : NSWindowController
@property (retain) IBOutlet NSArrayController* arrayController;
@end
@implementation SODocument
@synthesize dataModel = _dataModel;
- (id)init
{
self = [super init];
if (self)
{
// Make some fake data to bind to
NSMutableDictionary* item1 = [NSMutableDictionary dictionaryWithObjectsAndKeys: @"Item 1", @"attributeName", nil];
NSMutableDictionary* item2 = [NSMutableDictionary dictionaryWithObjectsAndKeys: @"Item 2", @"attributeName", nil];
NSMutableDictionary* item3 = [NSMutableDictionary dictionaryWithObjectsAndKeys: @"Item 3", @"attributeName", nil];
_dataModel = [[NSMutableArray arrayWithObjects: item1, item2, item3, nil] retain];
}
return self;
}
- (void)dealloc
{
[_dataModel release];
[super dealloc];
}
- (NSString *)windowNibName
{
return @"SODocument";
}
- (void)makeWindowControllers
{
SOWindowController* wc1 = [[[SOWindowController alloc] initWithWindowNibName: [self windowNibName]] autorelease];
[self addWindowController: wc1];
SOWindowController* wc2 = [[[SOWindowController alloc] initWithWindowNibName: [self windowNibName]] autorelease];
[self addWindowController: wc2];
}
- (void)addWindowController:(NSWindowController *)windowController
{
[super addWindowController: windowController];
[windowController addObserver:self forKeyPath: @"arrayController.selectionIndexes" options: 0 context: [SODocument class]];
}
- (void)removeWindowController:(NSWindowController *)windowController
{
[windowController removeObserver:self forKeyPath: @"arrayController.selectionIndexes" context: [SODocument class]];
[super removeWindowController:windowController];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([SODocument class] == context && [@"arrayController.selectionIndexes" isEqualToString: keyPath])
{
NSIndexSet* selectionIndexes = ((SOWindowController*)object).arrayController.selectionIndexes;
for (SOWindowController* wc in self.windowControllers)
{
if (![selectionIndexes isEqualToIndexSet: wc.arrayController.selectionIndexes])
{
wc.arrayController.selectionIndexes = selectionIndexes;
}
}
}
}
@end
@implementation SOWindowController
@synthesize arrayController = _arrayController;
-(void)dealloc
{
[_arrayController release];
[super dealloc];
}
@end
文档 nib 有一个 SOWindowController 的文件所有者。它有一个 NSArrayController 绑定到File's Owner.document.dataModel
,和一个 NSTableView ,其中一列绑定到ArrayController.arrangedObjects.attributeName
。
当我创建一个新文档时,会出现两个窗口,每个窗口都显示相同的内容。当我将 tableView 选择更改为一个时,另一个也会更改。
无论如何,希望这会有所帮助。