2015-05-13 11 views
8

Ich möchte die Elternknoten meines NSOultineView nicht sortieren.Wie kann ich verhindern, dass die übergeordneten Knoten meiner NSOutlineView sortiert werden?

Die Datenquelle meiner Gliederungsansicht ist ein NSTreeController.

Wenn ich auf eine Spaltenüberschrift klicke, möchte ich den Baum nur von der zweiten Hierarchieebene und ihren untergeordneten Elementen sortieren und die übergeordneten Knoten in der gleichen Reihenfolge belassen.

UPDATE So binde ich Spalten an die Werte und ordne den Sortierungsdeskriptor zu.

[newColumn bind:@"value" toObject:currentItemsArrayController withKeyPath:[NSString stringWithFormat:@"arrangedObjects.%@", metadata.columnBindingKeyPath] options:bindingOptions]; 
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:metadata.columnSortKeyPath ascending:YES selector:metadata.columnSortSelector]; 
    [newColumn setSortDescriptorPrototype:sortDescriptor]; 
+0

Haben Sie versucht, den benutzerdefinierten 'sortDescriptorPrototype 'der NSTableColumn-Instanz festzulegen, auf die Sie klicken? – Astoria

+0

@Astoria Ja! Ich habe das ausprobiert, und es funktionierte für diese Spalte (mit den Daten von den Elternknoten). Aber die Kinderknoten haben Daten in anderen Spalten, und es sollte möglich sein, die Kinder durch Klicken auf solche Spaltenköpfe zu sortieren. Wenn Sie jedoch auf solche Spalten klicken, wirkt sich dies auch auf die Reihenfolge der übergeordneten Knoten aus. – aneuryzm

+0

versuchen, 'sortDescriptorPrototype' für beide Spalten zu setzen. – Astoria

Antwort

2

Sie können einen benutzerdefinierten Vergleicher verwenden.

Um die Grundidee dieses Ansatzes zu demonstrieren, nehmen wir an, dass Sie NSTreeNode als Ihre Baumknotenklasse verwenden. Alle Wurzelknoten werden in einer NSArray namens gespeichert content und Ihre drei Wurzelknoten sind cat, dog und fish:

NSTreeNode *cat = [NSTreeNode treeNodeWithRepresentedObject:@"cat"]; 
// ...the same for dog and fish 
self.content = @[cat, dog, fish]; 

Dann erstellen Sie Ihre NSSortDescriptor Prototyp. Beachten Sie, dass Sie anstelle des zu vergleichenden Strings self als Schlüssel verwenden sollten (in diesem Fall representedObject), um Zugriff auf das rohe Knotenobjekt zu erhalten. Überprüfen Sie im Vergleich, ob das Objekt in Ihrem Root-Objekt-Array enthalten ist. Wenn YES, geben Sie einfach NSOrderedSame zurück, da es die Reihenfolge von der ursprünglichen Reihenfolge in Ihrem Array content unverändert lässt, andernfalls verwenden Sie die compare: Methode, um einen Standardvergleich durchzuführen.

NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:YES comparator:^NSComparisonResult(NSTreeNode *obj1, NSTreeNode *obj2) { 

    if ([self.content containsObject:obj1] && [self.content containsObject:obj2]) { 
     return NSOrderedSame; 
    } 

    return [obj1.representedObject compare:obj2.representedObject]; 
}]; 

[[outlineView.tableColumns firstObject] setSortDescriptorPrototype:sortDescriptor]; 

Edit 2

Wenn Sie mehrere Spalten haben, die separat sortiert werden müssen, können Sie nicht für jede Spalte self als Schlüssel verwenden, da der Schlüssel unter allen Spalten eindeutig sein sollte 'sortDescriptorPrototype s. In diesem Fall können Sie ein benutzerdefiniertes Objekt als representedObject erstellen und alle Ihre Daten einschließlich eines Zeigers zurück auf den Baumknoten im Objekt übertragen.

Edit 1

Die Richtigkeit des Ansatzes oben genannten erfordert NSOutlineView seine Reihen mit einem stabilen Art Algorithmus zu sortieren. Das heißt, identische Artikel behalten immer die gleiche relative Reihenfolge vor und nach dem Sortieren. Allerdings konnte ich in der Apple-Dokumentation keinen Hinweis auf die Stabilität des hier verwendeten Sortieralgorithmus finden, obwohl meiner Erfahrung nach der obige Ansatz tatsächlich funktionieren wird.

Wenn Sie sich unsicher fühlen, können Sie Ihre Stammbaumknoten explizit vergleichen, je nachdem, ob die aktuelle Reihenfolge aufsteigend oder absteigend ist. Dazu benötigen Sie eine ivar, um die aktuelle Bestellung zu speichern.Nur Umsetzung der outlineView:sortDescriptorsDidChange: Delegatmethode:

- (BOOL)outlineView:(NSOutlineView *)outlineView sortDescriptorsDidChange:(NSArray *)oldDescriptors { 
    ascending = ![oldDescriptors.firstObject ascending]; 
    return YES; 
} 

Und return NSOrderedSame ändern:

if ([self.content containsObject:obj1] && [self.content containsObject:obj2]) { 
    NSUInteger index1 = [self.content indexOfObject:obj1]; 
    NSUInteger index2 = [self.content indexOfObject:obj2]; 
    if (ascending) 
     return (index1 < index2) ? NSOrderedAscending : NSOrderedDescending; 
    else 
     return (index1 < index2) ? NSOrderedDescending : NSOrderedAscending; 
} 

bearbeiten 3

Wenn Sie nicht outlineView:sortDescriptorsDidChange: aus irgendeinem Grund implementieren können, können Sie manuell einen Beobachter heften sich an Das sortDescriptors-Array von outlineView:

[outlineView addObserver:self forKeyPath:@"sortDescriptors" options:NSKeyValueObservingOptionNew context:nil]; 

Auf diese Weise können Sie benachrichtigt werden, wenn der Benutzer auch auf die Kopfzeile klickt. Danach nicht vergessen, die folgenden Beobachtungsverfahren, als ein Teil der KVO Prozess zu implementieren:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 
    if ([keyPath isEqualToString:@"sortDescriptors"]) { 
     // Again, as a simple demonstration, the following line of code only deals with the first sort descriptor. You should modify it to suit your need. 
     ascending = [[change[NSKeyValueChangeNewKey] firstObject] ascending]; 
    } 
} 

Speicher undicht zu verhindern, müssen Sie diese Beobachter entfernen, bevor outlineView freigegeben wird (wenn nicht früher). Wenn Sie mit KVO nicht vertraut sind, überprüfen Sie bitte .

+0

hey danke sehr gute antwort. Ich habe gerade Ihre Bearbeitung gelesen: Leider kann ich outlineView nicht verwenden: sortDescriptorsDidChange: weil ich die Datenquellenmethoden nicht verwendet habe (ich bin stattdessen programmatisch die Werte binden). Wenn ich also eine Datenquelle hinzufüge, muss ich alle erforderlichen Methoden überschreiben. (Stattdessen studiere ich noch den Vergleichsansatz, komm bald zurück). – aneuryzm

+1

@Patrick Wenn Sie 'outlineView: sortDescriptorsDidChange:' nicht implementieren können, können Sie einen Beobachter manuell an das 'sortDescriptors' Array von 'outlineView' anhängen:' [outlineView addObserver: self forKeyPath: @ "sortDescriptors" Optionen: NSKeyValueObservingOptionNeuer Kontext: nil ]; 'Auf diese Weise können Sie benachrichtigt werden, wenn der Benutzer auch auf die Kopfzeile klickt. –

+0

Ja, cool. Ich benutze jetzt den Beobachter: aber wie könnte ich die Vergleichsfunktion an dieser Stelle einführen? Bitte beachten Sie das Update in meiner Frage, ich habe den Code hinzugefügt. Ich habe viele benutzerdefinierte Spalten, und ich bin verbindlich und für jede von ihnen einen anderen Sortierungsdeskriptor festlegen. – aneuryzm