all micro contact rss

Touch Bar Support and Interface Builder

One of the many challenges I faced while building x2y for Mac was getting touch bar support working. This is a very new feature, available only on the latest MacBook Pro machines, which are barely now just starting to ship. Nevertheless, I wanted my new app to support this new hardware right out of the gate.

As is the case with most new APIs, finding documentation and examples for NSTouchBar wasn’t as easy as it would have been for something that has been around much longer. There is a nice write up on Ray Wenderlich, of course, but it involved doing all the touch bar creation in code. I greatly prefer using Interface Builder whenever possible.

I could see clearly that Interface Builder objects existed for touch bar items. Xcode 8.1 has touch bars, spacers, buttons, popovers, color pickers, etc., ready to be dragged and dropped right into your storyboards. They wouldn’t exist if you couldn’t use them. But how? I couldn’t find any good write-ups on what to do once you’ve dragged them into the storyboard. Unlike view controllers, touch bars don’t get storyboard IDs, so there doesn’t seem to be any way to refer to an IB touch bar in code. How was I going to get a touch bar to show up, without ignoring these Interface Builder built-in tools and rolling the entire thing in code?

Determined to not do it the long way, I kept poking around until I finally stumbled across this video from Nick Walter on YouTube. In it, he shows that all you need to do is drag a touch bar item to your window controller, throw on the items you want, and it should automatically show up for that window. Wire the buttons to your first responder actions, and you’re good to go. Super easy.

But I had done that, to no avail. When I tried this in x2y, it wasn’t working. What was I missing? All I could see whenever I launched the app was the standard blank touch bar, with the lone keyboard popover button to show typing suggestions.

Typing suggestions. Wait a minute. My app basically always has an active NSTextField in focus. And NSTextField is one of those classes that gets “Automatic” support for standard touch bar typing suggestions. In other words, any NSTextField you put in your app will replace your custom touch bar with the standard typing suggestions bar. So my app was always overriding my custom touch bar from Interface Builder.

I needed a way to get my NSTextFields not to do that. Auto-correct and text suggestions make no sense in x2y, anyway, since you can only type numbers into the fields. So I didn’t mind losing the typing suggestions functionality.

A little more research, and I found the way to override the standard touch bar behavior for NSTextField: add an extension to NSTextView.

Why NSTextView? If you’re unfamiliar with macOS development, it’s important to know that NSTextFields themselves never become the First Responder in AppKit. Instead, when you click on a text field anywhere on the Mac, a hidden NSTextView called the field editor is what actually gets first responder status. So I needed to override NSTextView, not NSTextField, to get the standard touch bar to stop showing up.

Here’s the code I placed into my NSTextView extension file.

extension NSTextView {

     @available(OSX 10.12.1, *)
     override open func makeTouchBar() -> NSTouchBar? {

          let touchBar = super.makeTouchBar()
          touchBar?.delegate = self

          return touchBar
     }
}

Notice the @available ensures that this only runs on a Mac running 10.12.1, thus not crashing earlier versions of the OS that know nothing of touch bars.

I call super.makeTouchBar() to get the touch bar from my main window, as opposed to the standard text view bar. Simple enough.

Once I placed this into my app, my custom touch bar from Interface Builder stopped being overridden.

I know there are always times and situations where doing things directly in code makes more sense than Interface Builder. But for something as simple as adding a few buttons to a touch bar, it seems like a lot of extra steps to write it all out by hand, rather than dropping the objects into Interface Builder. Take a look at the tutorial from Ray Wenderlich, and see for yourself how much more work it is to accomplish a simple bar with a few buttons on it in code.

Luckily, Apple gives us the option to do things either way, even in the early days of a new class like NSTouchBar. The more I investigate this API, the more I’m convinced that touch bars were no afterthought on Apple’s part, but rather something that Apple carefully considered, not just from a hardware standpoint, but in software as well. I’m excited to see what new capabilities the touch bar gets over time.