2013-06-30 1 views
1

Каким образом можно получить rearrangeObjects для отправки в NSTreeController после изменения узлов в дереве? У меня есть пример приложения (полный код ниже) с использованием NSOutlineView и NSTreeController с простым деревом объектов Node.Правильный способ получения переупорядоченных объектов, отправленных в NSTreeController после изменения узлов в дереве?

В Версии 1 приложения, когда вы редактируете имя узла, дерево не используется, пока не нажмете заголовок столбца или не используйте пункт «Изменить порядок» в меню. Последний настроен так, чтобы напрямую отправлять объекты переадресации в NSTreeController.

В версии 2 я попытался отправить объекты переупорядочения из метода setName: Node. Это не похоже на хорошее решение, потому что это означает, что модель теперь имеет представление о представлении/контроллере. Кроме того, у него есть побочный эффект, когда контурное изображение теряет фокус после переименования (если вы выберете узел и отредактируете его имя, панель выбора превратится из синего цвета в серый) по какой-либо причине (почему?).

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

Я, по крайней мере в состоянии частично ответить на мой собственный вопрос, после того, как посмотрел на Apple, iSpend sample application. Их файл TransactionsController_Sorting.m включает в себя метод scheduleRearrangeObjects, который вызывает параметры переупорядочения по-другому. Изменение не мой собственный код таким же образом, означает включение этого фрагмента в SetName: метод:

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

С этим изменением, контурный вид больше не теряет фокус после переименования узла. Теперь осталось сделать этот код из модели и в view/controller; TransactionsController_Sorting, похоже, также иллюстрирует, как это сделать. (Я до сих пор не понимаю, почему выше изменение предотвращает вид контура от потери фокуса, хотя, кто есть объяснение?)

0

другого ответа, в качестве возможного объяснения

Я считаю rearrangeObjects и fetch не задерживаются до тех пор, следующая итерация runloop. fetch по крайней мере, говорит вам, так что в документации:

Особые соображения
Начиная с OS X v10.4 результат этого метода не откладывается до следующей итерации runloop так, что механизм представления ошибка может обеспечить обратную связь в виде листа.

В моих собственных экспериментах, я могу использовать dispatch_async после rearrangeObjects, чтобы получить код, выполняемый после Перестановки. Другими словами, если я не делаю dispatch_async код, следующий за rearrangeObjects, он будет применяться до отложенного перегруппировки. Это отличный способ вырвать волосы.

В любом случае, я подозреваю, что вы теряете фокус, потому что remrangeObjects сдувает контекст, в котором вы редактировали узел, когда он перезагружает все дерево объектов, но если вы заставите его немедленно выполнить, вы не потеряете это контекст.

Update here. Я имел дело с rearrangeObjects, и данные ядра не кажутся синхронными, и, конечно же, это не так. Я поймал arrayController, вызывающий dispatch_async через трассировку стека связывания.