2
  1. 我的窗口控制器子类是笔尖的所有者。
  2. 我在我的文档子类中用代码实例化我的数组控制器。文档和窗口控制器都在代码中使用它。
  3. 我像这样绑定表列:File Owner >> document._arrayController.arrangedObjects.attributeName
  4. 表视图不显示任何行。
  5. 窗口控制器和文档类都不接收-addObserver与表格视图相关的消息。

显然我没有正确绑定到这个数组控制器。我想我缺少一些关于表视图列如何绑定到数组的基本知识。

这个问题是在一些重构过程中出现的。我曾经在 nib 中实例化数组控制器。该文档是文件所有者,并且有一个阵列控制器的出口。绑定看起来像My Array Controller > mappedObjects > attributeName。一切正常。

由于文档通过数组控制器处理插入对象,我认为窗口控制器不应该负责创建它。窗口控制器是新的 nib 所有者,因此我将其从 nib 中删除。我现在在-makeWindowControllers. (我问了这个有关初始化的相关问题。)

在调试这个时,我发现了别的东西。如果窗口控制器是表视图的数据源并且我实现-numberOfRowsInDataSource

return [[self.document._arrayController arrangedObjects] count];

表视图调用它,-addObserver为所有列发送消息,并使用绑定实际加载每个单元格的值。但是,当为给定单元格加载值时,它不会加载第 n 个对象的属性值,而是加载整个对象列的arrangedObjects属性值。它将这些数组传递给值转换器(无法正确处理它们)并在文本单元格中显示数组(它们不适合)。description

当窗口控制器是表视图的数据源但列使用绑定时,表视图应该忽略 的结果-numberOfRowsInTableView,或者根本不应该调用它。(响应选择器return 0只是避免了运行时错误。数据源只是首先设置为实现单元格的工具提示。)再一次,当我在 nib 中创建数组控制器时,所有这些都可以正常工作。

一些想法:

  1. 甚至可以使用 IB 将表列绑定到另一个对象拥有的数组控制器吗?
  2. 我是否需要将阵列控制器放回笔尖,并让窗口控制器与文档实例共享它?(这听起来像可怕的设计。)
  3. 我应该有两个数组控制器,一个用于窗口控制器,另一个用于文档?

添加:

我使用表视图数据源和绑定的原因是使用这些方法实现拖放重新排序:

  • tableView:writeRowsWithIndexes:toPasteboard:
  • tableView:validateDrop:proposedRow:proposedDropOperation:
  • tableView:acceptDrop:row:dropOperation:
4

1 回答 1

1

一些想法:

首先,你不应该同时实现 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 选择更改为一个时,另一个也会更改。

无论如何,希望这会有所帮助。

于 2012-01-05T21:56:32.007 回答