macOS Dev: NSTableView

实现一个 NSTableView,NSTableCellVIew 根据内容量和 window 宽度自动适应高度。

NSTableView 和 UITableView 有很大不同。比如 iOS 在设置好 autolayout 之后就不用去管 cell 的高度了。但是 macOS 中仍然需要提供高度数据。其他诸如点击事件,响应 cell 的背景色等等 api 也和 iOS 不一样。由于 window 的大小可变,渲染图形的时候要考虑到这种情况,不能依赖 autolayout 一劳永逸。总之, macOS 要考虑的情况多一些,能找到的资料又相对欠缺。

NSTableView

  • Storyboard 中往 ViewController 拖入 NSTableView 控件,设置 autolayout 填满视图

  • 中栏层级结构图中右键拖放 Table View 至 View Controller,连接 NSTableViewDelegate 和 NSTableViewDataSource

  • 拖一个 tableView 的 IBOutlet 引用至 ViewController

  • 注册 Cell

      @IBOutlet weak var tableView: NSTableView!{
          didSet{
              let nib = NSNib(nibNamed: "CustomCell", bundle: nil)
              self.tableView.register(nib, forIdentifier: "CustomCell")
          }
      }
    

让 Cell 填满视图宽度

  • 选择 table view 后,Attributes inspector 中设置 Columns: 1,去掉 Headers, Reordering, Resizing 选项,Column Sizing: Uniform
  • 如果显示不正确,把 table view 拉到比两个 columns 还小的宽度,然后再拉回与 window 宽度同宽

NSTableCellView

  • 新建 CustomCell.swift
  • 新建 macOS > User Interface > Empty > CustomCell.xib,拖入 NSTableCellView,拖入一个 Custom View 作为 contentView,之后所有的空间都放在这个 contentView 上,稍后将用这个 contentView 来获得 cell 的高度
  • Attribute Inspector 中设置 CustomCell.xib 的类为 CustomCell,identifier > CustomCell,拖 IBOutlet 至 CustomCell.swift

把值显示到 cell 的两种方式

1. 绑定 Object

  • 选择 Table Cell View,Bindings inspector > value > Bind to: Table Cell View,Model Key Path: objectValue

2. 给 NSTableCellView 实例赋值

func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
    let cell = tableView.make(withIdentifier: "CustomCell", owner: self) as! CustomCell
    let item = dataSource?[row]
    cell.setContent(item: item)
    return cell
}

动态高度

通过容器视图的 fittingSize 方法获得高度

func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
    let cell = tableView.make(withIdentifier: "CustomCell", owner: self) as! CustomCell
    let item = dataSource?[row]
    cell.setContent(item: item)
    return cell.contentView.fittingSize.height
}

收取窗口改变的通知,在 window 大小改变的时候 reloadData()。

 NotificationCenter.default.addObserver(self, selector: #selector(receivedNotification(notification:)), name: NSNotification.Name.NSWindowDidResize, object: nil)

demo

参考

推荐阅读更多精彩内容