2
  1. 私のウィンドウ コントローラ サブクラスはペン先の所有者です。
  2. ドキュメントのサブクラスで、配列コントローラーをコードでインスタンス化します。ドキュメントとウィンドウ コントローラーの両方がコードで使用します。
  3. 次のようにテーブルの列をバインドします: File Owner >> document._arrayController.arrangedObjects.attributeName
  4. テーブル ビューには行が表示されません。
  5. ウィンドウ コントローラもドキュメント クラスも-addObserver、テーブル ビューに関連するメッセージを受け取りません。

明らかに、この配列コントローラーに正しくバインドしていません。テーブル ビューの列を配列にバインドする方法について、基本的なことが欠けていると思います。

この問題は、いくつかのリファクタリング中に発生しました。以前は nib でアレイ コントローラをインスタンス化していました。ドキュメントはファイルの所有者であり、アレイ コントローラのアウトレットがありました。バインディングはMy Array Controller > ArrangeObjects > attributeNameのように見えました。すべてがうまくいきました。

ドキュメントは配列コントローラを介してオブジェクトの挿入を処理するため、ウィンドウ コントローラがそれを作成する責任を負うべきではないと思いました。ウィンドウコントローラーはペン先の新しい所有者なので、ペン先から削除しました。のコードで作成します-makeWindowControllers。(私は初期化についてこの関連する質問をしました。)

これをデバッグしているときに、別のことを発見しました。ウィンドウコントローラーがテーブルビューのデータソースであり、実装する場合-numberOfRowsInDataSource:

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

テーブル ビューはそれを呼び出し、-addObserverすべての列のメッセージを送信し、バインディングを使用して各セルの値を実際に読み込みます。ただし、特定のセルの値をロードする場合、n 番目のオブジェクトの属性値をロードするのではなく、オブジェクトの列全体のarrangedObjects属性値をロードします。これらの配列を値トランスフォーマー (正しく処理できない) に渡し、配列をテキスト セル (収まらない) に表示します。description

ウィンドウ コントローラーがテーブル ビューのデータ ソースであるが、列がバインドを使用する場合、テーブル ビューは の結果を無視する-numberOfRowsInTableViewか、まったく呼び出してはならない場合があります。(セレクターに応答するとreturn 0、実行時エラーが回避されるだけです。データ ソースは、セルのツール ヒントを実装するために最初に設定されているだけです。 ) また、ペン先で配列コントローラーを作成したときに、これらすべてが正常に機能していました。

いくつかの考え:

  1. IB を使用して、テーブルの列を別のオブジェクトが所有する配列コントローラーにバインドすることは可能ですか?
  2. 配列コントローラを nib に戻し、ウィンドウ コントローラにそれをドキュメント インスタンスと共有させる必要がありますか? (それは恐ろしいデザインのように聞こえます。)
  3. ウィンドウ コントローラ用とドキュメント用の 2 つのアレイ コントローラを使用する必要がありますか?

追加した:

バインディングと共にテーブル ビュー データ ソースを使用した理由は、次のメソッドを使用してドラッグ アンド ドロップの並べ替えを実装するためです。

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

1 に答える 1

1

いくつかの考え:

まず、NSTableViewDataSourceプロトコルバインディングの両方を実装するべきではありません。通常はどちらか一方です。これを行う特別な理由がある場合は、最初にバインディングのみを使用してアプリを稼働させ、次にNSTableViewDataSourceから必要な機能を1ステップずつレイヤー化して、すべてが機能していることを確認します。ツールチップをサポートするために使用できるバインディングがあります。

次に、これが唯一のアプローチであるとは言いませんが、ArrayControllerをxibに戻すことをお勧めします。NSControllerサブクラスとそれらにバインドするコントロールの間には特別な関係があるようです-コントローラーキー:バインディングインスペクターのエントリは、使用していないときは無効になっているため、これを強く示唆しています。確かなことはわかりませんが、その大きなkeyPathをバインドしてドキュメントに戻り、arrayControllerを取得しても、その魔法は発生していないと思います。

また、NSArrayControllerを、コントロールがバインドされているウィンドウ以外の場所に配置する必要があるのはなぜでしょうか。また、それに関連して、WindowControllerがNSArrayControllerをドキュメントと共有するのはなぜですか?

NSArrayControllersは選択状態を保持するため、ウィンドウごとに1つ、またはより抽象的にはUIの近くに存在するため、1つを必要とする各ペン先に存在する必要があります。つまり、複数のウィンドウ間で単一の選択状態を共有するなど、型にはまらないことをしようとしている場合を除きます(つまり、ウィンドウAの選択を変更し、ウィンドウBの対応するコントロールもウィンドウAに一致するように選択を変更します)。これについては以下で説明しますが、要するに、同じ基になるデータに複数のarrayControllerをバインドするよりも、arrayControllerを共有したい他の理由は考えられません。

選択の共有が目標である場合は、ドキュメントで各ウィンドウのnibで作成されたArrayControllerのselectionIndexesにKey-Value Observancesを設定し、選択を他のウィンドウに伝播させるようなことを行う方がよいと思います。 'arrayControllers。

私はこれをコーディングしました。動作しているようです。Xcodeの標準のNSDocumentベースのCocoaアプリテンプレートから始めてdataModel、ドキュメントにプロパティを追加し、いくつかのデータで偽造しました。次に、の間に2つのウィンドウを作成しmakeWindowControllers、次に、それらの選択が互いに続くように、遵守事項などを追加しました。すべてがうまくまとまっているようでした。

1つのコードブロックに押しつぶされます:

#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のファイル所有者がいます。にバインドされFile's Owner.document.dataModelたNSArrayControllerと、にバインドされた1つの列を持つNSTableViewがありますArrayController.arrangedObjects.attributeName

新しいドキュメントを作成すると、2つのウィンドウが表示され、それぞれに同じものが表示されます。一方のtableView選択を変更すると、もう一方も変更されます。

とにかく、これが役立つことを願っています。

于 2012-01-05T21:56:32.007 に答える