2013-06-30 5 views
1

트리의 노드를 변경 한 후 rearrangeObjects을 NSTreeController로 보내려면 적절한 방법은 무엇입니까? NSOutlineView와 NSTreeController를 Node 객체의 간단한 트리와 함께 사용하는 샘플 애플리케이션 (아래 전체 코드)이 있습니다.트리에서 노드를 변경 한 후 NSTreeController로 보낸 rearrangeObjects를 얻는 올바른 방법은 무엇입니까?

응용 프로그램의 버전 1에서 노드 이름을 편집 할 때 열 머리글을 클릭하거나 메뉴에서 "재정렬"항목을 사용할 때까지 트리가 사용되지 않습니다. 후자는 rearrangeObject를 NSTreeController에 직접 보내도록 설정됩니다.

버전 2에서 노드의 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은 다른 방법으로 rearrangeObjects를 호출하는 scheduleRearrangeObjects 메소드를 포함합니다. 같은 방법으로 내 자신의 코드를 변경하면에서는 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 10.4이 메소드의 결과를 시작

특별 고려 사항
runloop의 다음 반복까지 지연되는 오류 프리젠 테이션 메커니즘 할 수 있도록 시트로 피드백을 제공하십시오. 내 자신의 실험에서

, 나는 다시 정렬 한 후에 실행되는 코드를 얻을 수 rearrangeObjectsdispatch_async를 사용할 수 있습니다. 즉, 내가 dispatch_async 코드 뒤에 오는 경우 rearrangeObjects 다음 코드는 지연된 재 배열 전에 적용됩니다. 머리를 찢을 수있는 좋은 방법입니다.

어떤 경우에도 rearrangeObjects는 전체 개체 트리를 다시로드 할 때 노드를 편집하는 컨텍스트를 날려 버리기 때문에 초점을 잃어 버렸다고 생각하지만 그 개체가 즉시 실행되도록 설정하면 잃지 않습니다. 문맥.

[편집] Update here. 나는 rearrangeObjects을 다루고 있었고 핵심 데이터는 동기가없는 것처럼 보였고, 물론 그렇다. 바인딩 스택 추적을 통해 dispatch_async를 호출하는 arrayController를 발견했습니다.