2013-06-30 34 views
1

在對樹中的節點進行更改之後,將rearrangeObjects發送到NSTreeController的適當方式是什麼?我有一個示例應用程序(下面的完整代碼)使用NSOutlineView和NSTreeController以及一個簡單的Node對象樹。更正樹中節點更改後,獲取重新排列的對象的正確方法發送到NSTreeController?

在應用程序的版本1中,當您編輯節點的名稱時,只有在單擊列標題或使用菜單中的「重新排列」項目後,纔會使用該樹。後者設置爲直接將重新排列的對象發送給NSTreeController。

在版本2中,我嘗試從Node的setName:方法發送rearrangeObjects。這看起來不是一個好的解決方案,因爲它意味着模型現在已經瞭解了視圖/控制器。另外,它具有副作用,即出於某種原因(爲什麼是這樣?)重命名後大綱視圖失去焦點(如果選擇節點並編輯它的名稱,選擇欄從藍色變爲灰色)。

NSArrayController有一個方法setAutomaticallyRearrangesObjects:但NSTreeController不?那麼解決這個問題的適當方法是什麼?

/* example.m 

    Compile version 1: 
     gcc -framework Cocoa -o Version1 example.m 
    Compile version 2: 
     gcc -framework Cocoa -o Version2 -D REARRANGE_FROM_SETNAME example.m 

*/ 

#import <Cocoa/Cocoa.h> 

NSTreeController *treeController; 
NSOutlineView *outlineView; 
NSScrollView  *scrollView; 

@interface Node : NSObject { 
    NSString *name; 
    NSArray *children; 
} 
@end 

@implementation Node 
- (id) initWithName: (NSString*) theName children: (id) theChildren 
{ 
    if (self = [super init]) { 
     name = [theName retain]; 
     children = [theChildren retain]; 
    } 
    return self; 
} 

- (void) setName: (NSString*) new 
{ 
    [name autorelease]; 
    name = [new retain]; 
#ifdef REARRANGE_FROM_SETNAME 
    [treeController rearrangeObjects]; 
#endif 
} 
@end 

NSArray *createSortDescriptors() 
{ 
    return [NSArray arrayWithObject: [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]; 
} 

void createTheTreeController() 
{ 
    Node *childNode1 = [[[Node alloc] initWithName:@"B" children:[NSArray array]] autorelease]; 
    Node *childNode2 = [[[Node alloc] initWithName:@"C" children:[NSArray array]] autorelease]; 
    Node *childNode3 = [[[Node alloc] initWithName:@"D" children:[NSArray array]] autorelease]; 

    Node *topNode1 = [[[Node alloc] initWithName:@"A" children:[NSArray arrayWithObjects:childNode1,childNode2,childNode3,nil]] autorelease]; 
    Node *topNode2 = [[[Node alloc] initWithName:@"E" children:[NSArray array]] autorelease]; 
    Node *topNode3 = [[[Node alloc] initWithName:@"F" children:[NSArray array]] autorelease]; 

    NSArray *topNodes = [NSArray arrayWithObjects:topNode1,topNode2,topNode3,nil]; 

    treeController = [[[NSTreeController alloc] initWithContent:topNodes] autorelease]; 
    [treeController setAvoidsEmptySelection:NO]; 
    [treeController setChildrenKeyPath:@"children"]; 
    [treeController setSortDescriptors:createSortDescriptors()]; 
} 

void createTheOutlineView() 
{ 
    outlineView = [[[NSOutlineView alloc] initWithFrame:NSMakeRect(0, 0, 284, 200)] autorelease]; 
    [outlineView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil]; 
    [outlineView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil]; 
    [outlineView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil]; 

    NSTableColumn *column = [[[NSTableColumn alloc] initWithIdentifier:@"NameColumn"] autorelease]; 
    [[column headerCell] setStringValue:@"Name"]; 
    [outlineView addTableColumn:column]; 
    [outlineView setOutlineTableColumn:column]; 
    [column bind:@"value" toObject:treeController withKeyPath:@"arrangedObjects.name" options:nil]; 
    [column setWidth:250]; 

    scrollView = [[[NSScrollView alloc] initWithFrame:NSMakeRect(0, 0, 300, 200)] autorelease]; 
    [scrollView setDocumentView:outlineView]; 
    [scrollView setHasVerticalScroller:YES]; 
} 

void createTheWindow() 
{ 
    id window = [[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 300, 200) 
     styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO] 
      autorelease];  
    [window cascadeTopLeftFromPoint:NSMakePoint(20,20)]; 
    [window setTitle:@"Window"]; 
    [window makeKeyAndOrderFront:nil]; 

    [[window contentView] addSubview:scrollView]; 
} 

void createTheMenuBar() 
{ 
    id menubar = [[NSMenu new] autorelease]; 
    id appMenuItem = [[NSMenuItem new] autorelease]; 
    [menubar addItem:appMenuItem]; 
    [NSApp setMainMenu:menubar]; 
    id appMenu = [[NSMenu new] autorelease]; 
#ifndef REARRANGE_FROM_SETNAME 
    id rearrangeMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Rearrange" 
     action:@selector(rearrangeObjects) keyEquivalent:@"r"] autorelease]; 
    [rearrangeMenuItem setTarget: treeController]; 
    [appMenu addItem:rearrangeMenuItem]; 
#endif 
    id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Quit" 
     action:@selector(terminate:) keyEquivalent:@"q"] autorelease]; 
    [appMenu addItem:quitMenuItem]; 
    [appMenuItem setSubmenu:appMenu]; 
} 

void setUpAutoReleasePoolAndApplication() 
{ 
    [NSAutoreleasePool new]; 
    [NSApplication sharedApplication]; 
    [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; 
} 

void activateAppAndRun() 
{ 
    [NSApp activateIgnoringOtherApps:YES]; 
    [NSApp run]; 
} 

int main(int argc, const char * argv[]) 
{ 
    setUpAutoReleasePoolAndApplication(); 
    createTheTreeController(); 
    createTheOutlineView(); 
    createTheWindow(); 
    createTheMenuBar(); 
    activateAppAndRun(); 
    return 0; 
} 

回答

1

我至少能夠在看過蘋果的iSpend sample application後回答我自己的問題。他們的文件TransactionsController_Sorting.m包含一個方法scheduleRearrangeObjects,以不同的方式調用rearrangeObjects。以同樣的方式改變自己的代碼意味着包括該片段中的setName:方法:

#ifdef REARRANGE_FROM_SETNAME 
    // Commented out: [treeController rearrangeObjects]; 
    [treeController performSelector:@selector(rearrangeObjects) withObject:nil afterDelay:0.0]; 
#endif 

隨着這一變化,大綱視圖不再失去焦點重新命名節點之後。現在要做的是將這些代碼從模型中取出並放入視圖/控制器中; TransactionsController_Sorting似乎也說明了如何做到這一點。 (我還是不明白,爲什麼上面的變化防止失去焦點,雖然,人有一個解釋大綱視圖?)

0

另一個答案,作爲一個可能的解釋

我相信rearrangeObjectsfetch被延遲,直到下一個runloop迭代。 fetch至少告訴你這樣的文檔:

特別注意事項
與OS X v10.4版本開始這個方法的結果被推遲到runloop的下一次迭代,這樣的錯誤陳述機制以表格的形式提供反饋。

在我自己的實驗中,我可以在rearrangeObjects之後使用dispatch_async來重新排列後執行代碼。換句話說,如果我沒有dispatch_async後面的代碼rearrangeObjects它將在延遲重排之前應用。這是撕掉你頭髮的好方法。

在任何情況下,我懷疑你正在失去焦點,因爲rearrangeObjects吹走在你正在編輯一個節點,因爲它重新加載整個對象樹的背景下,但如果你強迫它立即執行,你不會失去上下文。

[編輯] Update here.我正在處理rearrangeObjects和核心數據不同步似乎,果然,事實並非如此。我通過綁定堆棧跟蹤捕獲了一個調用dispatch_async的arrayController。

相關問題