原文地址 翻译:DeveloperLx
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.
If you do not see 16B2657, you can download the update from Apple .
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.
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.
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.
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.
Build and run the app, you’ll notice the Touch Bar is empty, aside from the System Button and Control Strip.
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:
-
Create a new
TouchBar
and set the delegate. -
Set the customizationIdentifier. Remember, every
TouchBar
andTouchBarItem
need to have unique identifiers. - Set the Touch Bar’s default item identifiers. This tells the Touch Bar what items it will contain.
- 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.
Yay! You got a… label. That’s not super helpful, though. It’s time to add some controls.
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:
- A new item was created to show a label for ratings.
-
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 thedelegate
, 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.
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:
- This creates a simple label, just like in previous steps.
- 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.
- 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.
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.
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:
- How to setup your app to show a Touch Bar
- How to present static labels in a Touch Bar
- How to add dynamic labels in a Touch Bar using binding
- 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!