How to Use NSTouchBar on macOS

Andy Pereira

After years of waiting and rumors, Apple finally released a set of new MacBook Pros. One of the new exciting things announced was the inclusion of a touch screen. Well… sort of.

The new Touch Bar replaces the traditional function keys found on all MacBooks in the past for a dynamic, multi-touch screen. And the best part is it’s fully open to developers, so you can use it to offer new ways to interact with your macOS applications.

If you’re a macOS developer, you’ll want to take full advantage of this new technology right away. In this tutorial, I’ll show you how you can use the new NSTouchBar API to easily create a dynamic touch bar for your macOS app.

Note: This tutorial requires Xcode version 8.1 or later. You will also need to ensure that you have macOS 10.12.1, build 16B2657 installed on your computer first. If you do not have this version, you will not be able to show the Touch Bar Simulator. To check, go to  > About This Mac, and click where you see 10.12.1. This will then show your build number.

About Mac

If you do not see 16B2657, you can download the update from Apple.

What is the Touch Bar?

As mentioned, the Touch Bar is a small touch screen that allows users to interact with apps (and their computer) in a whole new way.

Screen Shot 2016-10-31 at 12.44.31 PM

There are three default sections on the Touch Bar:

  • System Button: Depending on context, this will show a system level button, like Esc.
  • App Region: The default area provided for your app to show items.
  • Control Strip: This is a replacement of your familiar keys that you’d use to control screen brightness, volume, or music playback.

As with every new technology from Apple, there are a set of Human Interface Guidelines that you should follow when working with the Touch Bar. You should familiarize yourself with them here, as they are very important to maintaining a consistent pattern to your users.

Very briefly, here are a few sections of the guide that really stand out:

  • Don’t expose functionality just in the Touch Bar: This isn’t the place to keep things secret from users that haven’t upgraded their hardware yet. If you’re going to put it in the Touch Bar, make sure you can perform the action somewhere else in your app. Apple says that the Touch Bar can even be disabled, so don’t count on your users to always see it.
  • The Touch Pad is an extension of the Keyboard and Trackpad, not a display: Yes, it’s a screen, but it’s not a secondary display. Don’t distract the user with scrolling content or alerts.
  • Respond Immediately: When users tap a key on the keyboard, they expect immediate results. Similarly, when someone taps a virtual button on the touch bar, they also expect immediate results.

How Do I Support the Touch Bar?

To add support for the TouchBar in your apps, you use some new classes provided by Apple: NSTouchBar and NSTouchBarItem (and its subclasses).

Some of the NSTouchBarItem subclasses include features like:

  • Slider: Adjusts a value
  • Popover: Hide more functionality behind another item.
  • Color Picker: Pretty much says it all (a color picker if you didn’t catch it ;] ).
  • Custom: This is probably going to be your go-to item for a lot of things. It allows you to add simple labels, buttons, and all sorts of other controls.

You can customize your items quite a bit. From text size and color, to images, you can offer your users a modern approach to the keyboard that hasn’t been available before. Just remember the guidelines, and you should be good to go.

Getting Started

You’re probably ready to get started! To follow along, download this sample project here.

The application is a very simple Travel Log, that only does what is needed for the purposes of our tutorial. With the project open, go to Window > Show Touch Bar. You’ll now see the Touch Bar Simulator on your screen.

Initial Bar

Build and run the app, you’ll notice the Touch Bar is empty, aside from the System Button and Control Strip.

Starter App

First Bar

Before you can add anything to the Touch Bar, you’ll need to tell the system your application can customize the Touch Bar. Open AppDelegate.swift, and paste the following into applicationDidFinishLaunching(_:):

func applicationDidFinishLaunching(_ aNotification: Notification) {
  if #available(OSX 10.12.1, *) {
    NSApplication.shared().isAutomaticCustomizeTouchBarMenuItemEnabled = true
  }
}

This takes care of all the necessary validations and activation of your Touch Bar menu items for you. At the time of this writing the current version of Xcode does not have macOS 10.12.1 available as a deployment target, so you will need to place #available(OS X 10.12.1, *) around code or extensions dealing with the Touch Bar. Luckily, Xcode will give you a friendly error if you forget ;]

Open WindowController.swift, and look at makeTouchBar(). This method is checking if ViewController has a Touch Bar that can be returned. If so, it will send that Touch Bar to the Window, and be presented to the user. Right now, there is no Touch Bar being created, so nothing is shown.

Before you can go making your own touch bars, and touch bar items, you need to be aware that instances of these classes all require unique identifiers. Open TouchBarIdentifiers.swift to see how these have been created for this project. There are extensions for both NSTouchBarCustomizationIdentifier, and NSTouchBarItemIdentifier.

Go to ViewController.swift, and add the following at the end of the file, where the TouchBar Delegate is marked:

@available(OSX 10.12.1, *)
extension ViewController: NSTouchBarDelegate {
  override func makeTouchBar() -> NSTouchBar? {
    // 1
    let touchBar = NSTouchBar()
    touchBar.delegate = self
    // 2
    touchBar.customizationIdentifier = .travelBar
    // 3
    touchBar.defaultItemIdentifiers = [.infoLabelItem]
    // 4
    touchBar.customizationAllowedItemIdentifiers = [.infoLabelItem]
    return touchBar
  }
}

Here, you override makeTouchBar(), which is required for your view or window to create a touch bar. You also did the following:

  1. Create a new TouchBar and set the delegate.
  2. Set the customizationIdentifier. Remember, every TouchBar and TouchBarItem need to have unique identifiers.
  3. Set the Touch Bar’s default item identifiers. This tells the Touch Bar what items it will contain.
  4. Here, you set what order the items should be presented to the user.

You’re still not quite ready to see anything in your Touch Bar yet. You’ll need to tell the Touch Bar what the .infoLabelItem should look like. In the same extension, add the following:

func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
    switch identifier {
    case NSTouchBarItemIdentifier.infoLabelItem:
      let customViewItem = NSCustomTouchBarItem(identifier: identifier)
      customViewItem.view = NSTextField(labelWithString: "\u{1F30E} \u{1F4D3}")
      return customViewItem
    default:
      return nil
    }
}

By implementing touchBar(_:makeItemForIdentifier:), you can customize your touch bar items anyway you’d like. Here, you’ve created a simple NSCustomTouchBarItem, and set its view to an NSTextField. Build and run your application, and you’ll now see the Touch Bar has a new item.

TouchBar FirstItem

Yay! You got a… label. That’s not super helpful, though. It’s time to add some controls.

touchbar_ragecomic

Text Fields and Scrubbers

In makeTouchBar(), change the defaultItemIdentifiers to the following:

touchBar.defaultItemIdentifiers = [.infoLabelItem, .flexibleSpace, .ratingLabel, .ratingScrubber]

This will allow the Touch Bar to show three new items: a label and a scrubber. You’ve also added a .flexibleSpace. This is a dynamically sized space put in the Touch Bar to keeps things grouped together nicely. You can also take advantage of .fixedSpaceSmall, and .fixedSpaceLarge for more static sized spacing.

You’ll still need to customize these items, just like the label you added. Add the following cases to the switch in touchBar(_:makeItemForIdentifier:):

case NSTouchBarItemIdentifier.ratingLabel:
  // 1
  let customViewItem = NSCustomTouchBarItem(identifier: identifier)
  customViewItem.view = NSTextField(labelWithString: "Rating")
  return customViewItem
case NSTouchBarItemIdentifier.ratingScrubber:
  // 2
  let scrubberItem = NSCustomTouchBarItem(identifier: identifier)   
  let scrubber = NSScrubber()
  scrubber.scrubberLayout = NSScrubberFlowLayout()
  scrubber.register(NSScrubberTextItemView.self, forItemIdentifier: "RatingScrubberItemIdentifier")
  scrubber.mode = .fixed
  scrubber.selectionBackgroundStyle = .roundedBackground
  scrubber.delegate = self
  scrubber.dataSource = self
  scrubberItem.view = scrubber
  scrubber.bind("selectedIndex", to: self, withKeyPath: #keyPath(rating), options: nil)    
  return scrubberItem

Step by step:

  1. A new item was created to show a label for ratings.
  2. Here, a custom item is created to hold an NSScrubber. This is a new control introduced for the Touch Bar. They behave similar to a slider, but can be customized specifically for working in the bar. Since scrubbers require a delegate to handle events, all you need to do here is set the delegate, which ViewController already has implemented for you.

Build and run, and you’ll now see two new items in your Touch Bar. Notice that when you select an item from the scrubber, it will adjust the value in the app’s window.

Rating Items

Segmented Controls

Next, you’re going to add a segmented control to the application. Since this doesn’t work using the delegate pattern, you’ll get a chance to see how to set up a Target-Action within the Touch Bar. Back in makeTouchBar(), you’ll need to add the last three items to defaultItemIdentifiers:

touchBar.defaultItemIdentifiers = [.infoLabelItem, .flexibleSpace, .ratingLabel, .ratingScrubber, .flexibleSpace, .visitedLabelItem, .visitedItem, .visitSegmentedItem]

And add the last three cases to touchBar(_:makeItemForIdentifier:):

case NSTouchBarItemIdentifier.visitedLabelItem:
  // 1
  let customViewItem = NSCustomTouchBarItem(identifier: identifier)
  customViewItem.view = NSTextField(labelWithString: "Times Visited")
  return customViewItem
case NSTouchBarItemIdentifier.visitedItem:
  // 2
  let customViewItem = NSCustomTouchBarItem(identifier: identifier)
  customViewItem.view = NSTextField(labelWithString: "--")
  customViewItem.view.bind("value", to: self, withKeyPath: #keyPath(visited), options: nil)
  return customViewItem
case NSTouchBarItemIdentifier.visitSegmentedItem:
  // 3
  let customActionItem = NSCustomTouchBarItem(identifier: identifier)
  let segmentedControl = NSSegmentedControl(images: [NSImage(named: NSImageNameRemoveTemplate)!, NSImage(named: NSImageNameAddTemplate)!], trackingMode: .momentary, target: self, action: #selector(changevisitedAmount(_:)))
  segmentedControl.setWidth(40, forSegment: 0)
  segmentedControl.setWidth(40, forSegment: 1)
  customActionItem.view = segmentedControl
  return customActionItem

For each step:

  1. This creates a simple label, just like in previous steps.
  2. Here, you create another label, but you bind the value of the text to a property. Just like the scrubber, binding values to make updating Touch Bar items very easy.
  3. Finally, you create a segmented control to be displayed in a touch bar item. You can see that setting up a target and action is just the same as it always is.

Build and run, and you’ll see that you can interact with not only the scrubber, but the segmented control as well. Not only that, values changed in the Touch Bar are reflected in the window, and vice-versa.

Final Touch Bar

Colored Buttons

Finally, it would be nice to give the user a chance to save using the Touch Bar. Since this button has a different outcome from the others, you’ll take advantage of the new bezelColor property of NSButton to give it some color.

To do this, open TouchBarIdentifiers.swift, and in the NSTouchBarItemIdentifier extension, add the following to the end:

static let saveItem = NSTouchBarItemIdentifier("com.razeware.SaveItem")

This creates a new identifier from scratch, which will allow you to add a new button to the Touch Bar.

Go back to ViewController.swift, and add a new .flexSpace and .saveItem to the touch bar’s defaultItemIdentifiers:

touchBar.defaultItemIdentifiers = [.infoLabelItem, .flexibleSpace, .ratingLabel, .ratingScrubber, .flexibleSpace, .visitedLabelItem, .visitedItem, .visitSegmentedItem, .flexibleSpace, .saveItem]

You’re almost done – all you have left is to handle configuring the new item. In touchBar(_:makeItemForIdentifier:), add a final case before default:

case NSTouchBarItemIdentifier.saveItem:
  let saveItem = NSCustomTouchBarItem(identifier: identifier)
  let button = NSButton(title: "Save", target: self, action: #selector(save(_:)))
  button.bezelColor = NSColor(red:0.35, green:0.61, blue:0.35, alpha:1.00)
  saveItem.view = button
  return saveItem

Everything here should look pretty familiar to this point. All that is new is setting the bezelColor to a familiar green :].

Build and run, and you’ll see that you have a nice green button, and it has the same behavior as the Save button in the window.

Screen Shot 2016-10-31 at 5.33.58 PM (2)

Where To Go From Here?

You can download the final sample project here.

That’s it for learning the basics of the Touch Bar. It should be pretty clear Apple wanted to make this easy for you to get started to quickly make these features available to your users.

In this tutorial, you learned the following:

  1. How to setup your app to show a Touch Bar
  2. How to present static labels in a Touch Bar
  3. How to add dynamic labels in a Touch Bar using binding
  4. How to add controls to a Touch Bar, and handle their events

Don’t stop with these examples! There are plenty of exciting features to be found within NSTouchBar and NSTouchBarItem. Try adding a popover to your Touch Bar, or see how easy it is to format text in your app. You can also check out creating Touch Bars in Interface Builder.

If you have any questions, comments, or want to just want to rave (or complain) about the new MacBook Pro, please join the forum discussion below!

Team

Each tutorial at www.raywenderlich.com is created by a team of dedicated developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Andy Pereira

Andy is a Senior iOS Developer in Atlanta, GA for Delta Air Lines.

You can find Andy on LinkedIn or Twitter.

Other Items of Interest

Save time.
Learn more with our video courses.

raywenderlich.com Weekly

Sign up to receive the latest tutorials from raywenderlich.com each week, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

PragmaConf 2016 Come check out Alt U

Our Books

Our Team

Video Team

... 20 total!

Swift Team

... 15 total!

iOS Team

... 44 total!

Android Team

... 15 total!

macOS Team

... 11 total!

Unity Team

... 11 total!

Articles Team

... 15 total!

Resident Authors Team

... 17 total!

Podcast Team

... 8 total!

Recruitment Team

... 9 total!