如何在基于视图的NSOutlineView中自定义披露单元格

16

我正在尝试自定义视图为基础的NSOutlineView中的披露箭头外观。 我看到推荐使用

- (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item

使用代理方法来实现它。问题是出于某种原因没有调用此方法。我有两个自定义单元格视图 - 一个用于项,另一个用于标题项。也许这个方法不适用于基于视图的大纲视图?也许在 Lion 中出了些问题?

请给予一些指导。

4个回答

33

解决方案1:

子类化NSOutlineView并覆盖makeViewWithIdentifier:owner:方法。

- (id)makeViewWithIdentifier:(NSString *)identifier owner:(id)owner {
    id view = [super makeViewWithIdentifier:identifier owner:owner];

    if ([identifier isEqualToString:NSOutlineViewDisclosureButtonKey]) {
        // Do your customization
    }

    return view;
}

使用NSOutlineViewShowHideButtonKey来获取源列表。

解决方案2:

接口构建器

将按钮添加到列中,并将标识符设置为NSOutlineViewDisclosureButtonKey

enter image description here

NSOutlineView.h的官方文档

/* The following NSOutlineView*Keys are used by the View Based NSOutlineView to create the "disclosure button" used to collapse and expand items. The NSOutlineView creates these buttons by calling [self makeViewWithIdentifier:owner:] passing in the key as the identifier and the delegate as the owner. Custom NSButtons (or subclasses thereof) can be provided for NSOutlineView to use in the following two ways:
 1. makeViewWithIdentifier:owner: can be overridden, and if the identifier is (for instance) NSOutlineViewDisclosureButtonKey, a custom NSButton can be configured and returned. Be sure to set the button.identifier to be NSOutlineViewDisclosureButtonKey.
 2. At design time, a button can be added to the outlineview which has this identifier, and it will be unarchived and used as needed.
 
 When a custom button is used, it is important to properly set up the target/action to do something (probably expand or collapse the rowForView: that the sender is located in). Or, one can call super to get the default button, and copy its target/action to get the normal default behavior.
 
 NOTE: These keys are backwards compatible to 10.7, however, the symbol is not exported prior to 10.9 and the regular string value must be used (i.e.: @"NSOutlineViewDisclosureButtonKey").
 */
APPKIT_EXTERN NSString *const NSOutlineViewDisclosureButtonKey NS_AVAILABLE_MAC(10_9); // The normal triangle disclosure button
APPKIT_EXTERN NSString *const NSOutlineViewShowHideButtonKey NS_AVAILABLE_MAC(10_9); // The show/hide button used in "Source Lists"

当你有 //Do your customization 时会发生什么? - Clifton Labrum
@CliftonLabrum 这取决于你。你可以修改 view(由超类创建的 NSButton),或者创建并返回自己的视图。但是,如果你创建自己的视图,请务必设置标识符。 - WetFish
1
[(NSButton *)view setImage:[NSImage imageNamed:@"disclosure-closed"]]; - WetFish
这很有帮助,但我只想自定义第一行的披露按钮。由于此请求不是由委托的outlineView:viewForTableColumn:item:方法生成的,而是由outlineView的私有_updateDisclosureButtonForRowView:forRow:removeIfNotAvailable:updatePosition:inDidAddRowView:方法生成的,因此我该如何确定正在请求的视图的行或项目。 - Jon

12
This answer is written with OS X 10.7 in mind, for newer versions of OS X/macOS, refer to WetFish's answer 那个方法不会被调用,因为它只适用于基于单元格的大纲视图。
在基于视图的大纲视图中,展开三角形是可扩展行的行视图中的常规按钮。我不知道它在哪里添加,但它确实添加了,而NSView的didAddSubview:方法正好处理了这种情况,即在其他地方添加视图。
因此,子类化NSTableRowView,并像这样覆盖didAddSubview:。
-(void)didAddSubview:(NSView *)subview
{
    // As noted in the comments, don't forget to call super:
    [super didAddSubview:subview];

    if ( [subview isKindOfClass:[NSButton class]] ) {
        // This is (presumably) the button holding the 
        // outline triangle button.
        // We set our own images here.
        [(NSButton *)subview setImage:[NSImage imageNamed:@"disclosure-closed"]];
        [(NSButton *)subview setAlternateImage:[NSImage imageNamed:@"disclosure-open"]];
    }
}

当然,您的大纲视图的代理将必须实现outlineView:rowViewForItem:来返回新的行视图。
尽管名称如此,但NSOutlineViewframeOfOutlineCellAtRow:仍会被调用以获取基于视图的大纲视图,因此对于三角形的定位,您可能需要子类化大纲视图并覆盖该方法。

谢谢回复。所以outlineView:rowViewForItem基本上会在可展开单元格的情况下替换outlineView:viewForTableColumn吗? - Nava Carmon
我猜这取决于你想怎么做。我同时使用行视图来绘制背景(以及披露三角形),并使用单元格视图来显示内容。我相信这也是预期的方式,但视图本质上是灵活的。 - Monolo
我会尝试并告诉你结果。非常感谢! - Nava Carmon
非常感谢!在苹果列表上,他们建议填写一个关于缺乏适当的自定义界面和文档不足的错误报告。 - Nava Carmon
1
不要忘记调用 [super didAddSubview:] -- 否则,你会遇到一些麻烦... - corbin dunn

8

对于Swift 4.2 macOS 10.14,@WetFish的回答可以按照以下方式实现:

class SidebarView: NSOutlineView {

  override func makeView(withIdentifier identifier: NSUserInterfaceItemIdentifier, owner: Any?) -> NSView? {
    let view = super.makeView(withIdentifier: identifier, owner: owner)

    if identifier == NSOutlineView.disclosureButtonIdentifier {
      if let btnView = view as? NSButton {
        btnView.image = NSImage(named: "RightArrow")
        btnView.alternateImage = NSImage(named: "DownArrow")

        // can set properties of the image like the size
        btnView.image?.size = NSSize(width: 15.0, height: 15.0)
        btnView.alternateImage?.size = NSSize(width: 15.0, height: 15.0)
      }
    }
    return view
  }

}

看起来非常不错!


我按照你的答案操作了,成功添加了自定义分隔符。但是每组之间都有一条分隔线,我该如何去掉它? - prabhu

2

@Monolo的答案的Swift2版本:

override func didAddSubview(subview: NSView) {
    super.didAddSubview(subview)
    if let sv = subview as? NSButton {
        sv.image = NSImage(named:"icnArwRight")
        sv.alternateImage = NSImage(named:"icnArwDown")
    }
}

2
谢谢 - 现在我们只需要一个WetFish答案的Swift版本! - Monolo
1
你是个大佬,谢谢! 新方法名称: func didAddSubview(_ subview: NSView) - J.beenie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接