Building a NSTouchBar App
The new MacBook Pro with its fancy touch bar has been released by Apple. Being a developer, we always look to toy with the newest technology. In this tutorial, I will be building an app that uses the NSTouchBar API.
Requirements
We do not need the latest MacBook Pro to develop this app. All you will need is to be at least on the following:
- macOS Sierra 10.12.1 (Build 16B2657)
- Xcode Version 8.1
To check which version are you on, do this: ๏ฃฟ > About This Mac
. To see the Build
, click on the version number Version OS 10.12.1
.
The following tutorial is developed on macOS Sierra 10.12.2 (16C67)
and Xcode Version 8.2
.
Overview
We will be building a simple application that ask: โHow do you feel today?โ User will be able to express their feeling by touching a emoji button on the touch bar. A short text will be shown in order to response to their feeling.
Creating the project
From Xcode, select File > New > Project
. Click on macOS
tab and select Cocoa Application
. Then enter all the required detail.
Before we start, we should tell Xcode that we would like to show the Touch Bar
. Go to Window > Show Touch Bar
. Upon clicking Show Touch Bar
, a Touch Bar
should now appear on the screen.
Set Up
Next, we need to allow the customisation of Touch Bar
for our application. In AppDelegate
, do this:
func applicationDidFinishLaunching(_ aNotification: Notification) {
if #available(OSX 10.12.2, *) {
NSApplication.shared().isAutomaticCustomizeTouchBarMenuItemEnabled = true
}
}
By default, this value is set to false
. In order to customise the Touch Bar
we need to enable it. The line if #available(OSX 10.12.2, *)
is a check as we are using the Touch Bar
that does not exist in previous OSX version. Xcode will throw an error whenever we use something that is not available in previous version.
We will then create a HYFTWindowController
a subclass of NSWindowController
. Go to the Main
storyboard and update the Window Controller
class in inspector with our newly created HYFTWindowController
.
Start Coding Touch Bar
With all the setup completed, we are now ready to start writing code for the Touch Bar
. In HYFTWindowController
, do this:
@available(OSX 10.12.2, *)
override func makeTouchBar() -> NSTouchBar? {
guard let viewController = contentViewController as? ViewController else {
return nil
}
return viewController.makeTouchBar()
}
With HYFTWindowController
being our firstResponder
, the makeTouchBar()
would get called first. Here, we would pass the job to our ViewController
to create the Touch Bar
.
In ViewController
, override
the makeTouchBar()
like this:
@available(OSX 10.12.2, *)
override func makeTouchBar() -> NSTouchBar? {
let touchBar = NSTouchBar()
touchBar.delegate = self
touchBar.customizationIdentifier = .hyftBar
touchBar.defaultItemIdentifiers = [.infoLabelItem, .joyEmojiItem, .sadEmojiItem,
.angryEmojiItem, .flexibleSpace, .otherItemsProxy]
return touchBar
}
Here, we create a new NSTouchBar
and set ourself as its delegate
. The touchBar.customizationIdentifier
required a unique string to identify each touchBar
.
The touchBar.defaultItemsIdentifiers
expect an array of unique string identifiers to help us setup the item later. Note that we also pass in the .flexibleSpacing
and .otherItemsProxy
. These are a set of NSTouchBarItemIdentifer
provided by AppKit
. These help us set flexible space between item and allow other touch bar
with visible item to be nested inside.
A list of other default items can be found here.
In order to better manage our own identifier, I created a new file HYFTIdentifier
and have the following:
import AppKit
extension NSTouchBarItemIdentifier {
static let infoLabelItem = NSTouchBarItemIdentifier("com.zeta.InfoLabel")
static let joyEmojiItem = NSTouchBarItemIdentifier("com.zeta.JoyEmoji")
static let sadEmojiItem = NSTouchBarItemIdentifier("com.zeta.SadEmoji")
static let angryEmojiItem = NSTouchBarItemIdentifier("com.zeta.AngryEmoji")
}
extension NSTouchBarCustomizationIdentifier {
static let hyftBar = NSTouchBarCustomizationIdentifier("com.zeta.ViewController.HYFTTouchBar")
}
Creating NSTouchBarItem
Finally, we are all ready to create items on the touch bar. Is actually pretty straightforward to create items using the touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier:
method.
To make our code more readable, do this in ViewController
:
@available(OSX 10.12.2, *)
extension ViewController: NSTouchBarDelegate {
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) ->
NSTouchBarItem? {
let custom = NSCustomTouchBarItem(identifier: identifier)
switch identifier {
case NSTouchBarItemIdentifier.infoLabelItem:
let label = NSTextField.init(labelWithString: NSLocalizedString("How do you feel today?", comment:""))
custom.view = label
case NSTouchBarItemIdentifier.joyEmojiItem:
custom.view = NSButton(title: NSLocalizedString("๐", comment:""), target: self,
action: #selector(buttonPressed(_:)))
case NSTouchBarItemIdentifier.sadEmojiItem:
custom.view = NSButton(title: NSLocalizedString("๐", comment:""), target: self,
action: #selector(buttonPressed(_:)))
case NSTouchBarItemIdentifier.angryEmojiItem:
custom.view = NSButton(title: NSLocalizedString("๐ก", comment:""), target: self,
action: #selector(buttonPressed(_:)))
default:
return nil
}
return custom
}
}
The identifier
would help us identify which item are we going to be creating. We use a switch
here to setup each item differently. The order this get trigger will be the same as the order of item in the array passed into touchBar.defaultItemIdentifiers
.
The NSCustomTouchBarItem
have a view
that we should use for setting up custom item. For .infoLabelItem
, we just create a NSTextField
and update it. For the rest, we create a NSButtom
with different title
all of each will trigger a action buttonPressed(_:)
.
In order to prevent errors, we will create a empty buttonPressed(_:)
in the ViewController
and run the app for now. You should see this in your Touch Bar
.
Completing the App
What is left now is to complete the app. In the Main
storyboard, setup the ViewController
like this:
One NSTextField
on the top and another hidden NSTextField
near the bottom. Then add three NSButton
in the middle with the โ๐โ, โ๐โ, โ๐กโ as the respective title
.
Create a @IBOulet messageTextField
and hook it up to the bottom NSTextField
.Then hook up all three NSButton
action to the buttonPressed(_:)
method in ViewController
.
Next, copy and paste the following into ViewController
.
func getMessageBaseOnReaction(_ reaction: String) -> String {
switch reaction {
case "๐":
return "Being happy never goes out of style. - Lilly Pulitzer"
case "๐":
return "Sadness flies away on the wings of time. - Jean de La Fontaine"
case "๐ก":
return "To be angry is to revenge the faults of others on ourselves. - Alexander Pope"
default:
return "Look like our quote does not cater to this reaction"
}
}
//Quotes are extracted from https://www.brainyquote.com
func showMessageBaseOnReaction(_ reaction: String) {
messageTextField.isHidden = false
messageTextField.stringValue = getMessageBaseOnReaction(reaction)
}
//MARK: IBActions
@IBAction func buttonPressed(_ sender: NSButton) {
showMessageBaseOnReaction(sender.title)
}
The above are just code to update the messageTextField
with quote base on the action by the user.
Now, run the app again and play with the Touch Bar
.
Clicking any emoji item on the Touch Bar
or the button
on the window will both trigger the text to change. Now we have our very own Touch Bar
app ๐.
The completed project can be access via GitHub here.
Where to go from here?
We have only touch on the very basic of NSTouchBar
. There is a lot more customisation and control we could use to enhance user experience with the app. The following resources should be able to help you understand better: