没有使用NIB的基于视图的NSOutlineView?

10

NSOutlineViewNSTableView 的一个子类。目前,NSTableView 支持两种实现。

  • 基于单元格(Cell-based)。
  • 基于视图(View-based)。

要制作像 OSX 10.8 Finder 样式的侧栏(带有自动灰色图标样式),需要使用具有源列表突出显示样式的基于视图的表格视图。

使用 NIBs,这是很简单的工作。没有什么难度。(请参见 SidebarDemo)但我想避免任何 NIBs 或 Interface Builder。我想纯粹通过编程来制作侧栏。

在这种情况下,我有一个大问题。据我所知,并没有办法为特定的单元格提供原型视图。当我打开 .xib 文件时,我看到 <tableColumn> 包含 <prototypeCellViews>。并且这指定了将用于该列的视图。我无法找到如何使用公共 API 在编程方式下设置此内容。

作为解决方法,我尝试使用 -[NSTableView makeViewWithIdentifier:owner:]-[NSTableView viewAtColumn:row:makeIfNecessary:] 手动创建单元格,但它们都没有返回视图实例。我创建了一个 NSTableCellView,但它没有图像视图和文本字段实例。我也尝试设置它们,但这些字段被标记为 assign,因此这些实例会立即被释放掉。我试图通过强制保留它们来保持它们,但它不起作用。NSTableView 不管理它们,所以我确定表视图不喜欢我的实现。

我相信有一个属性可以为列设置此原型视图。但是我找不到它们。在哪里可以找到此属性并使用源列表样式的系统默认的 NSOutlineView 进行编程?


1
NSOutlineView的文档通常比较简略和不完整。 - uchuugaka
3个回答

6
如果您按照 SidebarDemo 中的示例,他们使用 NSTableCellView 的子类来处理详细行。为了模仿 InterfaceBuilder 的魔力,您可以在构造函数中将所有内容连接起来。其余部分与演示相同(请参见 outlineView:viewForTableColumn:item:)。
@interface SCTableCellView : NSTableCellView
@end

@implementation SCTableCellView

- (id)initWithFrame:(NSRect)frameRect {
  self = [super initWithFrame:frameRect];
  [self setAutoresizingMask:NSViewWidthSizable];
  NSImageView* iv = [[NSImageView alloc] initWithFrame:NSMakeRect(0, 6, 16, 16)];
  NSTextField* tf = [[NSTextField alloc] initWithFrame:NSMakeRect(21, 6, 200, 14)];
  NSButton* btn = [[NSButton alloc] initWithFrame:NSMakeRect(0, 3, 16, 16)];
  [iv setImageScaling:NSImageScaleProportionallyUpOrDown];
  [iv setImageAlignment:NSImageAlignCenter];
  [tf setBordered:NO];
  [tf setDrawsBackground:NO];
  [[btn cell] setControlSize:NSSmallControlSize];
  [[btn cell] setBezelStyle:NSInlineBezelStyle];
  [[btn cell] setButtonType:NSMomentaryPushInButton];
  [[btn cell] setFont:[NSFont boldSystemFontOfSize:10]];
  [[btn cell] setAlignment:NSCenterTextAlignment];
  [self setImageView:iv];
  [self setTextField:tf];
  [self addSubview:iv];
  [self addSubview:tf];
  [self addSubview:btn];
  return self;
}

- (NSButton*)button {
  return [[self subviews] objectAtIndex:2];
}

- (void)viewWillDraw {
  [super viewWillDraw];
  NSButton* btn = [self button];
  ...

没错。基本上,提供你的视图类。让你的代理知道你的视图类。确保你的视图类知道如何对齐自己和其子视图,当它们被添加和从OutlineView中删除时。这并不会使大纲视图变得更容易。它们仍然很麻烦。 - uchuugaka
这个可行!太神奇了!让它正常工作的关键是将文本视图和图像视图添加为NSTableCellView的子视图。此外,我不应该触碰框架和自动调整大小的东西。如果我触碰它们,NSOutlineView将无法正确布局它们。 - eonil

5

以下是@jeberle的代码,用Swift 4重新编写(五年后!):

class ProgrammaticTableCellView: NSTableCellView {
    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)

        self.autoresizingMask = .width
        let iv: NSImageView = NSImageView(frame: NSMakeRect(0, 6, 16, 16))
        let tf: NSTextField = NSTextField(frame: NSMakeRect(21, 6, 200, 14))
        let btn: NSButton = NSButton(frame: NSMakeRect(0, 3, 16, 16))
        iv.imageScaling = .scaleProportionallyUpOrDown
        iv.imageAlignment = .alignCenter
        tf.isBordered = false
        tf.drawsBackground = false
        btn.cell?.controlSize = .small
        // btn.bezelStyle = .inline                  // Deprecated?
        btn.cell?.isBezeled = true                   // Closest property I can find.
        // btn.cell?.setButtonType(.momentaryPushIn) // Deprecated?
        btn.setButtonType(.momentaryPushIn)
        btn.cell?.font = NSFont.boldSystemFont(ofSize: 10)
        btn.cell?.alignment = .center

        self.imageView = iv
        self.textField = tf
        self.addSubview(iv)
        self.addSubview(tf)
        self.addSubview(btn)
    }

    required init?(coder decoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    var button: NSButton {
        get {
            return self.subviews[2] as! NSButton
        }
    }
}
编辑:我发现了一个链接(它很可能会消失——它是在2011年最后修订的),这是苹果SidebarDemo的链接,@jeberle基于这个代码。

4
除了 @jeberle 的答案,我需要补充一些内容。
  • 保持文本字段和图像视图的关键是将它们作为 NSTableCellView 的子视图添加。

  • NSTableView.rowSizeStyle 设置为适当的值(非默认的 Custom)以使表格视图自动布局。否则,您必须完全自行布局。

  • 如果要使用预定义的 NSTableViewRowSizeStyle 值,请不要触摸 frameautoresizing。否则,可能会导致布局出现问题。

  • 您可以通过提供 private func outlineView(outlineView: NSOutlineView, heightOfRowByItem item: AnyObject) -> CGFloat 代理方法来调整行高。设置 NSTableView.rowHeight 不是一个好主意,因为它需要将 NSTableView.rowSizeStyle 设置为 Custom,这会关闭默认提供的单元格文本/图像布局管理功能。

  • 你可以通过设置 NSView.identifier 属性来重用行/单元格视图。 (示例)


感谢您提供gist的链接。我需要看到NSView.identifier被设置为makeView()返回nil,以使我的非nib tableview正常工作。 - WeakPointer

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