如何防止对 NSOutlineView 的 parent 节点进行排序?
How can I prevent the parent nodes of my NSOutlineView from getting sorted?
我不想对 NSOultineView 的 parent 个节点进行排序。
我的大纲视图的数据源是一个 NSTreeController。
单击列 header 时,我想仅从层次结构的第二层及其 children 对树进行排序,并将 parent 节点留在相同的顺序。
更新
这就是我将列绑定到值并分配排序描述符的方式。
[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];
您可以使用自定义比较器。
为了演示这种方法的基本思想,我们假设您使用 NSTreeNode
作为树节点 class。所有的根节点都存储在一个名为content
的NSArray
中,你的三个根节点分别是cat
、dog
和fish
:
NSTreeNode *cat = [NSTreeNode treeNodeWithRepresentedObject:@"cat"];
// ...the same for dog and fish
self.content = @[cat, dog, fish];
然后,创建您的 NSSortDescriptor
原型。请注意,您应该使用 self
作为键而不是您正在比较的字符串(在本例中为 representedObject
)来访问原始节点 object。在比较器中,检查 object 是否包含在根 objects 数组中。如果 YES
,只需 return NSOrderedSame
,因为它会保持 content
数组中的初始顺序不变,否则使用 compare:
方法执行标准比较。
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];
编辑 2
如果您有多个列需要单独排序,则不能使用 self
作为每个列的键,因为键在所有列的 sortDescriptorPrototype
中应该是唯一的。在这种情况下,您可以创建一个自定义 object 作为 representedObject
并将所有数据(包括指针)包装回 object.
中的树节点
编辑 1
above-mentioned 方法的正确性要求 NSOutlineView
使用 稳定排序 算法对其行进行排序。也就是说,相同的项目在排序前后总是保持相同的相对顺序。但是,我无法在 Apple 文档中找到任何关于此处使用的排序算法稳定性的证据,尽管根据我的经验,上述方法确实有效。
如果您觉得不安全,可以根据当前顺序是升序还是降序来明确比较您的根树节点。为此,您需要 ivar
来保存当前订单。只需实现 outlineView:sortDescriptorsDidChange:
委托方法:
- (BOOL)outlineView:(NSOutlineView *)outlineView sortDescriptorsDidChange:(NSArray *)oldDescriptors {
ascending = ![oldDescriptors.firstObject ascending];
return YES;
}
并将return NSOrderedSame
更改为:
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;
}
编辑 3
如果您出于某种原因无法实施 outlineView:sortDescriptorsDidChange:
,您可以手动将观察者附加到 outlineView 的 sortDescriptors 数组:
[outlineView addObserver:self forKeyPath:@"sortDescriptors" options:NSKeyValueObservingOptionNew context:nil];
通过这种方式,您也可以在用户单击 header 时收到通知。之后不要忘记实现以下观察方法,作为 KVO 过程的一部分:
- (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];
}
}
为了防止内存泄漏,您必须在 outlineView
被释放之前(如果不是更早的话)移除这个观察者。如果您不熟悉 KVO,请务必查看 Apple's guide.
我不想对 NSOultineView 的 parent 个节点进行排序。
我的大纲视图的数据源是一个 NSTreeController。
单击列 header 时,我想仅从层次结构的第二层及其 children 对树进行排序,并将 parent 节点留在相同的顺序。
更新 这就是我将列绑定到值并分配排序描述符的方式。
[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];
您可以使用自定义比较器。
为了演示这种方法的基本思想,我们假设您使用 NSTreeNode
作为树节点 class。所有的根节点都存储在一个名为content
的NSArray
中,你的三个根节点分别是cat
、dog
和fish
:
NSTreeNode *cat = [NSTreeNode treeNodeWithRepresentedObject:@"cat"];
// ...the same for dog and fish
self.content = @[cat, dog, fish];
然后,创建您的 NSSortDescriptor
原型。请注意,您应该使用 self
作为键而不是您正在比较的字符串(在本例中为 representedObject
)来访问原始节点 object。在比较器中,检查 object 是否包含在根 objects 数组中。如果 YES
,只需 return NSOrderedSame
,因为它会保持 content
数组中的初始顺序不变,否则使用 compare:
方法执行标准比较。
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];
编辑 2
如果您有多个列需要单独排序,则不能使用 self
作为每个列的键,因为键在所有列的 sortDescriptorPrototype
中应该是唯一的。在这种情况下,您可以创建一个自定义 object 作为 representedObject
并将所有数据(包括指针)包装回 object.
编辑 1
above-mentioned 方法的正确性要求 NSOutlineView
使用 稳定排序 算法对其行进行排序。也就是说,相同的项目在排序前后总是保持相同的相对顺序。但是,我无法在 Apple 文档中找到任何关于此处使用的排序算法稳定性的证据,尽管根据我的经验,上述方法确实有效。
如果您觉得不安全,可以根据当前顺序是升序还是降序来明确比较您的根树节点。为此,您需要 ivar
来保存当前订单。只需实现 outlineView:sortDescriptorsDidChange:
委托方法:
- (BOOL)outlineView:(NSOutlineView *)outlineView sortDescriptorsDidChange:(NSArray *)oldDescriptors {
ascending = ![oldDescriptors.firstObject ascending];
return YES;
}
并将return NSOrderedSame
更改为:
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;
}
编辑 3
如果您出于某种原因无法实施 outlineView:sortDescriptorsDidChange:
,您可以手动将观察者附加到 outlineView 的 sortDescriptors 数组:
[outlineView addObserver:self forKeyPath:@"sortDescriptors" options:NSKeyValueObservingOptionNew context:nil];
通过这种方式,您也可以在用户单击 header 时收到通知。之后不要忘记实现以下观察方法,作为 KVO 过程的一部分:
- (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];
}
}
为了防止内存泄漏,您必须在 outlineView
被释放之前(如果不是更早的话)移除这个观察者。如果您不熟悉 KVO,请务必查看 Apple's guide.