Blog

Using NSTableView inside Xcode Playgrounds

Playgrounds are a fast way to prototype UI elements, including table views, which are best added programatically

Playgrounds were introduced in Xcode 7 and they are a great way to quickly test ideas. At first they were non-interactive, but since they now allow interaction I often use them to test table views and other complex controls.

The venerable NSTableView shares plenty of code with its cousin in UIKit but has a few more quirks due to its advanced age. With UIKit, adding a table view to a playground is easy. We just drag out a UITableViewController, subclass it and override the methods to return the number of items we need. However, there is no NSTableViewController equivalent we can use in AppKit. And while loading NIB files inside a Playground is possible, you have to compile the NIB before the Playground can read it.

Thus I find it quicker to create the table view programmatically. And at the very least, it gives a good understanding of the moving parts of a table view and how they fit together.

Behind the scenes of NSTableView

It helps to understand how a table view is generated. The Xcode visual debugger shows the layers that make up a table view. These are automatically generated when you drag out a new table view in Interface Builder.

An exploded NSTableView shows the five layers that Interface Builder automatically generates

From left to right the relevant views are the NScrollView in blue, two views which make up the content background, then NSClipView and NSTableView.

An NSScrollView handles user scrolling along the axis that we need. It provides the scrubber and horizontal and vertical scrolling tracks.

The NSClipView ensures that the document view of the scroll view is clipped to its bounds. It also draws the over and underscroll background and hence AppKit has added the second and third layers.

Finally, our NSTableView class is the last in the view stack and draws the rows, the table header and footer and all the subviews.

Data source and delegate

We’ll need a helper class to handle the NSTableViewDataSource and NSTableViewDelegate methods.

import AppKit
import PlaygroundSupport

class TableViewDelegate: NSObject, NSTableViewDelegate, NSTableViewDataSource {
  func numberOfRows(in tableView: NSTableView) -> Int {
    return 30
  }
  
  func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
    return 20
  }
  
  func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
    let view = NSTextField(labelWithString: "Cell \(row + 1)")
    return view
  }
}

This is the bare minimum to get a functioning table view. We set the number of rows and return a textfield as we want this to be a view based table.

Now we initalize the table view and the scroll view manually.

let containerFrame = NSRect(x: 0, y: 0, width: 250, height: 500)
let tableViewDelegate = TableViewDelegate()
let scrollView = NSScrollView(frame: containerFrame)
let tableView = NSTableView(frame: containerFrame)
let column = NSTableColumn()

column.width = containerFrame.size.width

tableView.headerView = nil
tableView.dataSource = tableViewDelegate
tableView.delegate = tableViewDelegate
tableView.addTableColumn(column)

scrollView.documentView = tableView

PlaygroundPage.current.liveView = scrollView

We create an instance of our helper class, then we create the table view. We also need to create at least one table column or our viewFor tableColumn method will never get called.

Finally we set the documentView of our scroll view and set the playground live view to our scroll view.

And that’s all there is to it, a live table view inside a Playground. Have fun learning by hacking around with table views.

A live table view inside a Xcode playground

Found this useful? Take a look at more articles about macOS development.