objective-c - 如何防止对 NSOutlineView 的父节点进行排序?

标签 objective-c macos cocoa nsoutlineview

我不想对我的 NSOultineView 的父节点进行排序。

我的大纲 View 的数据源是一个 NSTreeController。

单击列标题时,我想仅从层次结构的第二层及其子节点对树进行排序,并以相同的顺序保留父节点。

更新 这就是我将列绑定(bind)到值并分配排序描述符的方式。

    [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 作为您的树节点类。所有的根节点都存储在一个名为contentNSArray中,你的三个根节点是catdog:

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

然后,创建您的 NSSortDescriptor 原型(prototype)。请注意,您应该使用 self 作为键而不是您正在比较的字符串(在本例中为 representedObject)来访问原始节点对象。在比较器中,检查该对象是否包含在您的根对象数组中。如果 YES,只返回 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中应该是唯一的>s。在这种情况下,您可以创建一个自定义对象作为 representedObject,并将所有数据(包括指向对象中树节点的指针)包装起来。

编辑 1

上述方法的正确性要求 NSOutlineView 使用稳定排序 算法对其行进行排序。也就是说,相同的项目在排序前后总是保持相同的相对顺序。但是,我无法在 Apple 文档中找到任何关于此处使用的排序算法稳定性的证据,尽管根据我的经验,上述方法确实有效。

如果您觉得不安全,可以根据当前顺序是升序还是降序来明确比较根树节点。为此,您需要一个 ivar 来保存当前订单。只需实现 outlineView:sortDescriptorsDidChange: 委托(delegate)方法:

- (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];

通过这种方式,您也可以在用户单击标题时收到通知。之后不要忘记实现以下观察方法,作为 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 .

关于objective-c - 如何防止对 NSOutlineView 的父节点进行排序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30215363/

相关文章:

iphone - Zombies工具如何运行iPhone程序?

ios - 如何在 iOS 中添加带有可滚动自定义 View Controller 的可滚动标签栏

linux - 如何在 *nix 中登录时运行脚本?

objective-c - cocoa Hello World 屏保

cocoa - Swift 字符串和 cocoa 绑定(bind) : placeholder string is not displayed

python - 将 "NS.time"从 plist 转换为实际时间

objective-c - 如何创建不同宽度和高度的平铺照片库?

java - GLSL 不受支持的版本

macos - cmd 命令 "runas"在 MacOS 中等效

iphone - 将对象传递给 NSThread 选择器