The new Force Touch trackpad adds a whole new dimension to user interactions. OS X 10.11 introduces system controls that support pressure behaviors. Understand how to integrate with these behaviors and support them in custom controls. See how to use the Taptic Engine to provide subtle physical feedback based on trackpad input. Hear best practices for adopting new swiping gestures.
RALEIGH LEDET: Good morning.
Welcome to Session 217: Adopting New Trackpad Features.
My name is Raleigh Ledet, I'm an AppKit engineer and I'm master
of using the new Force Touch trackpads,
so this is what we're going to be talking about,
the new Force Touch trackpads.
They're very, very cool pieces of technology.
What makes them different than our previous trackpads is
that there isn't a physical button for you to press on.
Instead, we have these four force sensors
so we can measure how much force the user is applying
to the trackpad and then we marry
that with our Taptic Engine.
And when we've determined
that the user has pressed enough force on the trackpad
to issue a mouse down, we go ahead and use the Taptic Engine
and we yank the trackpad sideways just a little bit,
and the user gets the feeling
that they have actually pressed down on a button.
And so, the trackpad moves sideways,
but your brain makes you think
that you have actually pressed down on a button.
It's a really awesome sensation and it's really neat
on how your mind is tricked
into believing you've pressed a button.
We have some of these downstairs in the lab,
if you haven't experienced one of these yet,
please come down and try it out.
Another quick look at the Taptic Engine going there --
So, to recap, you get a little bit of pressure
and you get a click, but allows us to recognize
when you apply more pressure to the trackpad and we can sense
that as what we call a Force click,
and you'll get a little bit more haptic feedback
from that as well.
So, you can do lots
of interesting things with the Force click.
In fact, let me show you some
of the interesting things you can do with that now.
I have up here, you can see as I'm moving the cursor around,
you can see the force that I'm applying to the trackpad.
If I go down to the next level, you get down to Force click.
And so, everybody was able to hear that?
I've added -- you know, normally your computer doesn't play a
sound when you do Force click; I've added that in
because you can't actually feel the haptic feedback since you're
over there and I'm over here.
So, you'll be able to watch as I do this demo the force
that I'm applying to the trackpad.
One of the things that you can do is renaming files
in the Finder; sometimes it's kind of tricky,
you have to click on it, and you have to do it a second time
in such a way that -- and I missed it again.
You got to do the right timing
so you don't double-click and open it.
But with the Force Touch, you just Force click on it,
it goes right immediately into editing the filename.
It's a lot easier to do; that's just one of the small things
that you can do with Force click.
I'm going to open up TextEdit real quick.
Let me grab the window.
You have seen this example as well, you can Force click
and you can get Quick Look to come up.
It's kind of neat, you can actually sit here, you can play
around with the animation if you adjust your force levels.
That's another feature that we have.
Lastly to show you some things real quick, you know,
I could change how fast by varying the force,
how fast the photos come in, or changing the amount
that the indicator is showing,
you can control the amount of pressure here.
And of course, no pressure demo would be complete
without having a drawing field that you can draw on as well.
Lastly, don't have the sound hooked up for this,
but on this control when it gets --
when you rotate the photo back to zero,
you're feeling a little bit of haptic feedback
on the trackpad for that.
So that's pretty neat.
Let's switch back to the slides.
We've got a lot to cover.
We're going to talk about the APIs,
that that application was using, the various APIs that we use
in the system as well, so that you can go ahead
and add all sorts
of new features using the Force Touch trackpad
in your application.
I'm going to teach you today how
to become a master using the Force Touch trackpad on OS X.
To become a master in the Force Touch trackpad,
even though we're going to do it in one day,
you've got to start off small,
you've got to start off learning everything
that a squire would know about using the Force Touch trackpad,
and this is all about using the high-level APIs
and the built-in tools that we have in some of our controls.
The we're going to move on, and we're going
to learn everything a knight needs to know
about using the Force Touch trackpad.
This is all about the flow of the force through the system,
the event stream, we'll talk about customizing Spring Loading
in your application and then taking it all the way
to doing some Alignment Feedback
and providing some additional haptic feedback to the user.
Then finally, we're going to cap it off with becoming a master
of using the Force Touch trackpad and this is
about controlling the force.
Configuring the trackpad
so it is doing the appropriate haptic feedbacks
for the given situation for your controls
and manually playing haptic feedbacks where appropriate.
So let's dig in.
In being a squire we're going to talk about Table Row Actions,
it's a nice, neat new feature, you have seen
that in the Mail application demo
that Craig did during the keynote.
We'll talk about spring-loaded controls,
some things that are built in, and the accelerator controls.
We're going to do that by looking at some case studies.
Here is Mail, for example, and if you do a two-finger swipe
on a row in Mail, you get some more Table Row Actions
that could come up, and you can click on them,
or you can do a smooth swipe, a little bit longer swipe
and it will actually activate the default action
and so it is one complete gesture for the user.
It is a really nice way
of adding some additional functionality
to your application.
I use it every day in Mail myself.
The API for it is incredibly simple.
In your tableView delegate,
just implement tableView rowActionsForRow edge,
and we will tell you what edge it is, it's either the leading
or the trailing, so we handle right to left
and left to right for you.
And then you just return back an array of Table Row Actions.
Create a new NSTable Row Action, you init with a style, a title,
and a handler, the handler's what's going to get called back
if that item is selected, either by clicking on it
or if they did a complete swipe for the default one,
it goes ahead and the handler is called.
We have a couple of styles: Regular and Destructive.
Regular is the one with the blue highlighting,
and that's what you should use for most styles
of your Table Row Actions.
Destructive is the one that's red.
Don't choose these because of their color;
it is more than just red.
The destructive items, since by nature they're destructive we
actually make it a little bit harder
for the user to do a full swipe.
They have to swipe a further distance
on the trackpad before a destructive action is triggered
This is so that they don't accidentally trigger it.
Use regular for almost everything
and reserve destructive items just for destructive, don't try
and get the red color, there is some important semantics there
that you need to be aware of.
And that's all there is to this API.
It's that simple.
This works on a new Force Touch trackpad and it also works
on our legacy trackpads and the Magic Mouse,
so you can easily add these to your application
and you can open up some great new possibilities.
Let's look at Spring Loading in the Finder.
So I have this image of Lola that I downloaded,
and I want to move her to my Documents folder.
Some I'm going to start dragging and go back in my history
by Force clicking, I'm even going to change it to icon mode
so I can find the Documents folder easier,
and then finally go ahead and drop it in my Documents folder.
You might have known about spring loading before
where you could hover over a folder and after a timeout,
the folder would go ahead and spring load.
We have added spring loading to a lot more places
and applications to bring windows forwards,
and as you saw the buttons were spring loaded,
and you can bypass the hover timeout
by just doing a Force click,
and it becomes much more intuitive and easier to use.
To implement Spring Loading
in those toolbar buttons all Finder did was set the Spring
Loading property of NSButton to true.
Really what they did was just check the box in IB.
It is that easy.
You can do that for segmented control as well;
when I change the icon layout from list view to icon view,
that's on NSSegmentedControl,
and again it's just a springLoaded property
and you can set it with a check box in IB.
It's that simple to turn Spring Loading on in your buttons
and your segmented controls.
You have to opt in for this, but for places
that are doing navigation during drag
and drop it is really useful and I suggest
that you go and turn it on.
There is another example of using force; this is QuickTime.
I want you to pay attention to the fast-forward button here.
I'm going to use the Force Touch trackpad and I'm going
to apply different pressure to the button,
and you can see I can go up to 5x, 10x, 30x.
You can back off.
As you're moving through your movie file,
you can control how fast you're moving forward,
so you slow down when you get close to that area
that you're looking for without overshooting.
The way QuickTime does this is we have a new button type,
so the fast-forward buttons are really literally just NSButtons
with a custom image, and they set the button type
to either AcceleratorButton or MultiLevelAcceleratorButton.
For AcceleratorButton -- you can set it right here in IB --
as the force in the trackpad changes,
the button will continually send its action message,
so as the force changes, you get a new action message;
when the force changes again, you get a new action message,
and the range of the doubleValue is going to be 1
when the user clicks the button up to 2
as the user presses the maximum amount of force
on the trackpad that we accept.
You can see the pressure change between 1 and 2
and you can adjust however you --
whatever you need to do with that.
In QuickTime's case, that controls the acceleration.
You will finally get a value of 0.
A final action message with a value of 0
when the user ends tracking of the button.
When they release the mouse button up,
you'll get a last action message with a range of zero.
Now, what QuickTime is actually using here is the
You can set that right here in IB.
The MultiLevelAcceleratorButton is discrete;
whereas the AcceleratorButton is a smooth range,
a continuous range between 1 and 2,
the AcceleratorButton is integer levels,
it's a discrete integer levels, and you can set
that with the maxAcceleratorLevel.
Our range is between 1 and 5 so you can set how many levels
that you want to have in your acceleration;
by default it is 2.
QuickTime sets it to 5.
Then again, you look at the doubleValue.
Now the range is going to be 0 to 5, it's 1 when you click,
as the user goes up through the levels,
it'll go up to whatever you set the max level to,
and you'll get a final message action of 0
when the user finishes tracking.
And this is what QuickTime is doing.
Here you can see how QuickTime is just mapping 1, 2, 3, 4,
and 5 to your various speeds,
and for example 3 is 10x fast-forwarding.
Another example of Accelerator Controls: here is Maps,
and Maps has this nice zoom buttons,
and these are actually implemented
NSSegmentedControls has a new tracking mode
which is MomentaryAccelerator, and this works exactly
like the accelerator NSButtonType does --
real easy to set that in IB as well.
But instead of asking for the doubleValue
when the segmented control action messages are fired,
you want to ask for the doubleValue
for the selected segment.
It is the same range as we discussed earlier;
it's 1 to 2 with a final action message with a value of 0
when tracking has ended.
Segmented controls don't have a multilevel option,
they only support the continuous mode.
Here is an example of Photos.
For moving through your photos in Photos by applying,
varying the pressure,
I can control how fast the photos move across.
This is something that we call a continuous accelerator control.
If you have an accelerator control settings
on either NSButton or NSSegmentedControl,
in the NSControl section you can set the continuous flag to true
or just check the box in IB,
and you'll get what we call a continuous accelerator control.
Continuous accelerator controls are different
because you don't worry about their doubleValues so much.
You just want to move to the next slide as soon
as the action happens.
The doubleValue doesn't matter.
They come back, come in on a heartbeat,
and the force changes the frequency of that heartbeat.
That's the difference.
To drive the point home a little bit, let's compare the two.
In accelerated control,
ou get your action message whenever the pressure happens
to change, so there may be a little bit of delay in there,
it may come close together,
and you just change how fast you're fast-forwarding
in the movie.
For a continuous control it comes in on a heartbeat
for a continuous acceleration,
and then you just do your action.
The frequency, they might come together closely
if the frequency is high because the user is applying a lot
It is a great way for doing something like sliding photos
where the animation speed is constant, but when do you need
to bring in the next photo?
That's the next time the action message fires
and it gives the user a lot of control.
That's everything you need to know to be a squire.
You are all now squires in using the Force Touch trackpads.
We have talked about Table Row Actions, a very easy API
to implement, spring-loaded NSButtons
and accelerator NS Buttons and segmented controls,
they're real easy to turn on, our high-level API,
you get a lot of bang for your buck here in using these APIs
and we hope that you turn them on in your applications.
Let's move forward to being a knight.
Being a knight is all
about understanding how the force flows through the system.
We're going to talk about the force event stream,
and then I'm also going to talk
about the spring-loading protocol
so that you can use the same API that NSButton is doing
to provide spring loading in your custom applications.
Then finally we will talk about some Alignment Feedback API
to help you do snap-to guides and things like that.
Let's talk about the event stream.
We have a ton of events already on OS X; you get the mouse downs
and ups, your gestures for magnify and rotate.
And now we're introducing another one:
Or actually, we introduced it in 10.10.3
when we introduced the new MacBooks,
so you can get NSEventTypePressures there
The are pressure gesture.
And along with the new event type, of course,
we have the event mask to go along
with it: NSEventMaskPressure.
And what this means is that during your tracking loops,
you can just add event mask pressure
to your tracking loop mask,
and you can get the pressure events coming
in as you're tracking the mouse as well.
It is really easy to use.
If you prefer to use the responder approach
where you're overriding mouse down and mouse drag
and mouse up, we also have a new responder method
pressureChangeWithEvent, so you can get them that way
as well if you prefer.
Let's dig into the properties of the pressure gesture.
It is a gesture; unlike a mouse event sequence
which has individual types for mouse down, mouse drag,
and mouse up, there is just the single type for pressure,
it has a phase, and it goes through a cycle of Began,
Changed, and Ended when the gesture ends.
And we have a stage.
A stage is how we determine when a Force click happens.
So when you do a mouse down with a trackpad you'll get a Began
phase pressure gesture and it will have a stage of 1.
This is the standard click level stage.
The user presses harder to get to Force click level
on the trackpad, the stage will change to 2.
That's how you know the user's accomplished Force click
and you can just immediately do your Force click action and go
into rename on the Finder, for example.
Then as the user releases the force from the trackpad,
it goes back to stage 1 as it gets to click level,
and eventually it gets to stage 0 when the gesture ends
because the mouse button down, it is no longer down,
so you get a final event of stage 0 with a phase of Ended.
Now, of course, no pressure gesture would be complete
without actually having a pressure value,
so we have a pressure property as well,
it is within the range of 0 to 1.
It is important to note here
that the pressure property is the pressure
of the current stage.
So as you can see in the chart,
as you enter stage 1 the pressure starts to go up
and reaches 1 as you approach stage 2, and once you cross
over to stage 2, the pressure drops immediately back
down to 0, and it goes back up again
as you increase the pressure while you're in stage 2.
The pressure is of the current stage.
That's really important.
Now I'm showing a linear mapping between the force
on the trackpad to what the user is doing to the values
in the pressure event, but I don't want you
to read too much into that.
We like to think of every click on the trackpad
as a new adventure, and we look at a lot
of different variables -- is the user using their thumb,
what firmness setting do they have in their preferences,
how are they interacting with the trackpad --
so we dynamically change these curves on the fly
to give the user the best possible experience,
and we normalize the input from the trackpad
into the pressure range of 0 to 1
and that's what you should be using in your application.
As you notice when I was clicking around earlier
in the demo, just clicking around in TextEdit,
I wasn't getting the animation of the popover until I started
to really get close to having enough pressure
to reach Force click.
You don't want to have a whole bunch
of distracting animations occurring
when the user's just clicking around.
Doing this animation as they're approaching the transition
to stage 2 can be useful.
This is what the stage transition property is
As you see here, it is in the range of 0 to 1 just
like pressure, but it stays at 0 for a much longer period of time
until you start to approach the next stage,
stage 2 in this case.
I want you to get to approach stage 2, it will then shoot
up to 1, you can use this range to control your animation,
and it won't interfere with just clicking
around in your interface.
You can get that animation and as soon
as you reach stage 2 you can pop that animation to completion,
and you can see the stageTransition value drops back
to 0 once you reach stage 2, and we don't have a stage 3
in this case to transition to, so it just stays at 0
for the remainder of the time.
I mentioned earlier that mouse events are going
on at the same time as pressure gestures are.
The trackpad is still emulating a mouse like it always has.
It is also issuing pressure gestures.
I'm going to look at how those flow in the system
at the same time in parallel.
This is what we're going to cover.
This is an example of the user putting force on the trackpad,
applying a click, going all the way
to the Force click threshold,
and then releasing pressure off of that.
That's their input.
Down here we're going to show you the events as they're coming
into your application.
So the user starts off, they're applying light pressure,
and these are all mouse moves.
We haven't reached a click threshold yet.
We haven't even started a pressure gesture yet.
These are just mouse moves,
there are no pressure gestures coming in.
You reach the click threshold, a mouse down occurs,
and you also get a pressure began event with a stage of 1
and the pressure value is going to be 0 at this point.
We don't guarantee
if the pressure began event occurs first or if the mouse
down occurs first; they can swap sometimes.
The easiest way to handle that is just look for the mouse down
and if you also want pressure events, then start looking
for them after the mouse down occurs.
As the user starts to apply more force to the trackpad,
you see the pressure rises up towards 1
as we approach the Force click threshold.
If the cursor is moving, these are going to come
through as mouse dragged events,
you have the mouse dragged events
and the pressure events -- we're still in stage 1 --
we reach the Force click threshold,
you now get a pressure gesture with a stage 2
and the pressure value drops all the way back to 0.
The user continues to apply more force on the trackpad,
the pressure starts to increase again, as they start
to release the pressure from the trackpad it starts
to go back down toward 0.
Now they have released it back to the Force click threshold,
that pressure is at 0.
We're still at stage 2 at this point.
As the user releases a bit more pressure,
we're still at stage 2.
We have actually exaggerated it a little bit on this graph.
But it is very difficult for somebody to hold pressure
at a constant rate on the trackpad.
If they're right at the Force click level they'll be going
above it and slightly below it and we don't want
to be triggering Force click on and off, on and off, on and off;
that's not what the user's trying to do.
We require that you drop below the Force click threshold a
little bit further before we finally unlatch from stage 2,
and we give you finally an event of stage 1.
And during that small section
of time the pressure is going to be 0 in the event.
You're obviously below the Force click threshold
where the pressure would start to go up.
Then now that you're in stage 1, the user continues to release
and the pressure jumps up and starts to come back down again.
You notice it didn't jump all the way back up to 1,
because that's part of the little gap.
One of the things to note here is that this is one
of the reasons why you shouldn't try to combine stage 1
and stage 2 to get a larger dynamic range,
we're going to talk more about that when we get
to the master section.
Whenever there's stage 2 occurs,
automatically we also provide haptic feedback, so don't try
and combine the pressure into two stages.
Either look at stage 1 and use the pressure or look at stage 2
and use the pressure if that's where you need
to do your animation or the stage transition property.
We reached the click threshold point for stage 1,
so your pressure is now back down to 0, and just like we did
with the Force click threshold, the user actually has
to release even more pressure
from the trackpad before we finally release from stage 1.
We're going to do a pressure with a phase ended
at stage 0 and the mouse up.
Again, whether the mouse up occurs first
or the pressure ended event occurs first,
that is not guaranteed; the easiest way to deal
with this is look for the mouse up, track your pressure
and your mouse movements at the same time
until the mouse up occurs.
Just ignore any pressure events that might occur before
or after that sequence.
This will work with mice as well as our older trackpads
and the new Force Touch trackpads as well.
Finally, if the user is moving around with very,
very light pressure after the mouse up, these are mouse moves,
the pressure gesture has ended and we're starting the cycle
over again as we did in the beginning.
It gets real important to know sometimes on the mouse
down if there is going
to be pressure associated with this mouse down.
Is this coming from a device that's emulating the mouse
and also issuing pressure gesture events.
The way we have to help you do this is
by using the associatedEventMask property
on the mouse down events.
You can see this on the mouse dragged events as well.
It is real easy to use.
You ask the mouse down event for the associatedEventMask,
you find out if it contains the EventMaskPressure,
if you're using Cocoa you're just ending it
with the NSEventMaskPressure
because you're checking the bit field.
If it does contain the EventMaskPressure,
you know pressure events are going to be coming,
and you can set it up so that you have varying brush widths
for example in your drawing.
And that's great.
If it doesn't contain the EventMaskPressure,
then it is coming from a mouse or an older style trackpad
for example, and at that point you want
to choose a default brush size,
usually you choose the maximum brush size at that point
or maybe you want to just choose half a brush size
and you use this constant value
for the entire mouse dragging sequence.
That's the new EventTypePressure,
it's got a lot of new properties, it's got a phase,
because it is a gesture, pressure which is
within the range of 0 to 1 for the current stage.
Of course you have stage, you can easily see
when the user goes to Force click, stage transitions
for animating those transitions, the associated event mask
so you know when your mouse events are going
to have pressure associated with them,
and of course the pressure change
with event responder method
if you're using the responder methods.
Let's talk a little bit about spring loading
and how NSButton implemented spring loading;
you can use that exact same API in your custom controls
to add spring loading to your application.
There is an NSSpringLoadingDestination
protocol; it's very similar to NSDraggingDestination protocol
if you've ever implemented that.
In your destination, in your destination view you need
to implement either springLoadingEntered
To give you an example of how these work,
NSButton implements springLoadingEntered
and not springLoadingUpdated,
because a button is either enabled or disabled,
so as soon as you enter the button,
it can return the spring-loading options and it is not going
to change for the lifetime
that the drag is occurring in the button.
NSSegmentedControl, on the other hand,
Each segment may be enabled or disabled independently
of each other, it's one view, so with springLoadingUpdated,
segmented control can watch the drag and find out exactly
where in the control the drag is
and dynamically change the spring-loading options.
As I mentioned, you need to --
if you implement one of these methods,
you need to implement spring-loading options.
You can implement both if you want, you don't have
to implement just one of these, you need to implement
at least one of these though.
For your spring-loading options, it is pretty obvious,
you return if spring loading should be enabled
or disabled, that's pretty easy.
We also have a couple of other interesting options,
continuous activation --
generally a spring-loading action is discrete,
spring loading occurred,
the button fires its action, it is over.
There is also a continuous version
which we'll cover a little bit more
in the next couple of slides.
I want to move on to no hover.
As I mentioned earlier, spring loading can be triggered either
with a hover, which works great for people
that don't have Force Touch trackpads
or you can Force click.
If you want to do a Force click on a canvas for example
that has a large area, and the user is just dragging the item
across the canvas, they're just trying to get across the canvas,
and they lift the finger up to come back
down to continue the drag, or lift the mouse
up to continue the drag,
that amount of time might be just long enough
that the cursor stays still that spring loading is activated.
So this would be a false activation,
that wasn't what the user was trying to do.
If you have a situation where you're getting a lot
of false activations because of hover, then you might want
to consider the no hover spring-loading option
which will still allow users with the Force Touch trackpad
to do a Force click to get spring loading
in that area of your view.
Use it sparingly, make sure that you really think about it
because we don't want to leave out any users
that don't have a Force Touch trackpad,
but if you're getting more false activations,
this is a good option to use sometimes.
Along with springLoadingEntered and Updated,
of course we have springLoadingExited --
this lets you know when the drag has exited your view --
and there's also draggingEnded.
Spring loading is part of the drag and drop operations,
so if the user has dragged over your view
and your spring-loading destination
and you implement draggingEnded,
when the dragging does completed, when the user lets go
of the mouse, cancels the drag,
you will get back the draggingEnded.
Whether this drag, whether the user completed the drag
in your application
or in another process, it doesn't matter.
Everybody that's implemented draggingUpdated will get
Since this is part of dragging,
the NSDraggingDestination also has a draggingEnded function.
It is the exact same function we have here.
If you are both a Spring Loading destination
and an NSDraggingDestination, you only need
to have one implementation of draggingEnded,
it applies to both, so you'll need to do any cleanup you need
to do for both Spring Loading and dragging destination
if you're both a Spring Loading destination
and a dragging destination at the same time.
Now we get to the really fun stuff about spring loading.
So you're required to implement springLoadingActivated.
This is where we tell your destination
that the user has spring loaded.
We have a Boolean value, which is normally yes.
As I mentioned, spring loading is a discrete action,
it has occurred, and NSButton just fires its action
and everybody is happy.
But if you have that continuous bit set, then as soon
as the user Force clicks,
we'll send a springLoadingActivated yes,
you can start a timer, you can add continuously
on that timer firing your action message, and when you release
from Force click it will respond with a springLoadingActivated no
and that you know to turn off your timer at that point.
If the user is using hover,
you will get the springLoadingActivated
at the hover timeout with a yes, and you'll get a no
when they move out of the control.
Again to compare this to the normal discrete action,
usually spring loading from Force click occurs
on the release of Force click, so they move all the way
down into Force click, it's
when they release Force click you will get a
springLoadingActivated yes, if you're not using continuous;
that's the one that we generally suggest
that you use unless you need
to continuously fire your action message during a spring load.
Lastly, we have springLoadingHighlightChanged.
We like to give the user feedback on what's going on.
We have three forms of highlighting: None,
Standard, and Emphasized.
When you get a springLoadingHighlightChanged
message, you need to ask the draggingInfo for what
that springLoadingHighlight is, set your view
that needs display, and then you update and you draw
with the correct highlighting.
What's important here is that you don't try to infer any kind
of behavior that the user is doing with this yet.
That's what springLoadingActivated is for.
Sort of like when you select an item from a menu we blink
that menu item to let the user know and confirm
that they have selected that item, we'll do something similar
to that in spring loading,
and we do that by changing the highlighting,
and so all you have to do is just draw
with the new highlighting style whenever requested
and you'll get a consistent look in your application
to match the rest of the system.
Use springLoadingActivated to know
when to fire the action messages.
That's Spring Loading Destination.
We have Alignment Feedback, you can see this
in Interface Builder when you get two items together,
we snap them together, draw that nice little alignment guide.
We have some new API to help you do this.
The drawing is up to you, but our new API helps you decide
when to do the snapping.
To give you an example of that, let's look at a tracking loop,
a typical tracking loop.
Let's zoom in on that.
You get your mouse down event,
you figure out what your event mask is, which events you want
to track, you ask for the next event, you move your item,
update your data model, set your needs display,
is this a mouse up, no, and you just continue the cycle,
you draw whenever drawRect is called and you drag your item
across the screen until the mouse up occurs.
We have an NSAlignmentFeedbackFilter object
to help you out with this.
You get your mouse down, the first thing you do,
you get the input event mask
from the Alignment Feedback filter.
This is the events that the Alignment Feedback filter needs
to know about, you just or them or union them in the Swift case
with your event mask for when you call nextEventMatchingMask.
Once you get the next event, the very first thing you do,
you give that event to the Alignment Feedback filter,
it's going to return right away,
it just updates some internal state.
You move -- excuse me, if you're using a pan gesture recognizer,
you can also update with the pan gesture recognizer --
it works very similar to a tracking loop,
everything applies except for this one message difference.
Once Alignment Feedback filter returns
from processing the input, you move the item in your data model
as you normally would, and then you prepare alignment.
Let's dig in to prepareAlignment a bit and you're going
to use the Alignment Feedback filter to help you do that.
We have the object in the data model previously,
we got the event, and we decided the user moved it to here,
this is the default location
that if we don't do any snapping,
this is where the object is going to end up.
We want to know, should we snap it here, should we snap it
down to this line, or should we snap both at the same time
and get it down in the corner.
Use the Alignment Feedback filter object to help
to decide this,
(without space), previousX, alignedX, and defaultX,
these coordinates are in the window coordinates space
so it works really well regardless of your zoom level
that you might have in your view.
If snapping should occur, we will go ahead
and return you an Alignment Feedback token.
If you don't get an Alignment Feedback token,
don't do any alignment.
If you get an Alignment Feedback token,
in your data model change the X position in this case
to the aligned position and hang on to
that Alignment Feedback token.
You can then check for vertical movement as well,
except you send your Y values instead of your X
and again you may or may not get an Alignment Feedback token.
If you've gotten two Alignment Feedback tokens you'll have
aligned in both axes and so your object will be
down here in the corner.
Then sometimes -- this happens more rarely,
but sometimes you only want to snap to a position
if it is both aligned on the X axis and the Y axis,
and it's either aligned to both axes or aligned to neither,
in that case we have Alignment Feedback token for movement
and you pass in points instead of an individual X
or Y coordinate, and it works the same way, you may
or may not get an Alignment Feedback token.
You iterate over the various items that are being dragged
that they can snap to, you get back your Alignment Feedback
tokens and you change your data model if snapping should happen,
and now you have a collection of tokens.
You want to perform haptic feedback
to the user using the new Force Touch trackpad.
We'll use the Alignment Feedback filter to help us do that;
we'll just ask the Alignment Feedback filter
to perform feedback at a performance time,
just use the default for the performance time for now,
we'll cover performance times a little bit more later.
Then you just pass the array a feedback token.
You can even pass an empty array if nothing was aligned
and Alignment Feedback filter is robust with that,
it knows to just do nothing.
Then you set that your view needs display
and you redraw whenever you're asked; if you have the tokens,
then you know when you're redrawing
to also draw alignment guides if that's appropriate for your UI.
The feedback will then be performed synchronously
with the screen change and the user will have a nice
synchronous -- sees the alignment guide pop up
and they feel the haptic feedback
from the trackpad at the same time.
If the user is not using a Force Touch trackpad the Alignment
Feedback filter knows how to work with that as well,
and so you just have to write it
as if there is a Force Touch trackpad;
if there's not, it just works great.
So that's all there really is
to using Alignment Feedback filters.
They're pretty simple APIs, real easy to add it
to your application and existing tracking loops,
provided a consistent feel across app,
we look at the velocity of the cursor, for example,
so that we don't do snapping if the user is moving quickly,
because we don't want
to do Alignment Feedback either at that point.
That's not what the user is trying to do,
we look at the modifier values,
and so this will provide a consistent feel
across all applications that are using the system-wide Alignment
And you can use this for more than just dragging a item;
if you're doing a resize for example or a size to fit,
that's another good place to use Alignment Feedback filter.
It could be used in a lot of different places.
So that's everything you need to know to be a knight.
You're all knights in using the Force Touch trackpad.
Let's move on to becoming masters.
This is all about controlling the force.
This is about configuring the trackpad
so that it works appropriately for your custom situations
and manually providing haptic feedback.
Let's go back to that drawing example.
You start to do a drawing and you press
on the Force Touch trackpad, you get a Force click
and that's not really appropriate in your drawing.
And as I mentioned earlier, you don't want to try
to combine the pressure values from stage 1
with with the pressure values from stage 2, it's not going
to be even during the release, and the user's going
to get this haptic feedback in the middle of the drawing,
that's not a good experience.
So we want to configure the trackpad
to not provide Force click actuations at all in that case.
And this is what we can do
with the pressure configuration object.
We initialize one with a pressureBehavior,
check out the header file and the documentation,
check out NSEvent.h, there's a lot of description
in the header file about each of these,
I'm not going to cover them all.
The default one is DeepClick, that's what happens
by default in the system.
In this case we want PrimaryGeneric;
PrimaryGeneric is a one-stage gesture,
so the user won't get a Force click actuation
when they're drawing,
and it provides the largest dynamic range of user input
of their force on the trackpad, mapping that back out to you
and to pressure between 0 and 1.
It's the best one to use for drawing and for a lot
of other situations, and then check the header files
for the descriptions of the other behaviors to see
which one is appropriate for your situation.
Once you have a pressure configuration,
you just call set.
The trackpad is now configured in this new configuration
and everything is great -- with some caveats.
You can only set the trackpad
into a different configuration during a mouse drag,
so on mouse down, you check the mouse location,
if it is supposed to be changed
for this specific mouse location,
create a pressure configuration, you call the set,
and the trackpad is going to be changed.
You need to realize that you're racing the user here.
The user might move the cursor over your view, go immediately
to a Force click, release the mouse,
and you didn't even get the mouse down yet,
perhaps you're being paged in from virtual memory
and your app is not responsive.
You can try to set the pressure, the trackpad configuration
at this point but the user is already completed their gesture,
and it won't take effect.
When you do set the trackpad configuration it is
automatically reset back to the system default
when the user ends the gesture as well, so you don't have
to worry about unsetting it.
But this isn't ideal for most situations; it is really useful
when you need to decide at the very last minute based
on the mouse location what the configuration should be.
Instead, just set the pressure configuration on NSView.
Create a pressure configuration ahead of time,
set it as the pressure configuration property
on the NSView and the system will go ahead and set
up the trackpad before mouse down even occurs.
In fact, the system can configure the trackpad
to this configuration even
if your app isn't being responsive yet.
Now the user can go ahead and interact with your application,
you didn't even get the mouse down yet,
but they didn't feel the Force click because it is
in the PrimaryGeneric behavior and the events you get
in from the application, from the system at that point
when you finally do get your events
if you're not being responsive, don't have Force click in them,
they don't go to stage 2.
Let's talk a little bit about haptic feedback.
Haptic feedback should be used sparingly.
This is for subtle interactions.
We just want the trackpad to just feel right, right?
So when the user is trying to align something,
they get that haptic feedback and it feels great,
we're not trying to massage the user's finger here,
we just want this to be subtle interaction.
In fact, if the user goes back to one
of our older trackpads we want them
to maybe not even realize why something is wrong
but it just doesn't quite feel right.
That's the point of using the haptic feedback is doing it
appropriately when the user is trying to do something
to give them that little bit of subtle feedback,
oh yes, this is just right.
This is how it should have always been,
and I didn't know that.
You just ask the NSHapticFeedbackManager
for the defaultPerformer, always ask for the defaultPerformer
because it can change based on the input device
and the user's preferences.
You ask to perform the feedback pattern
at a specific performance time, we have three --
three patterns: Generic, Alignment, and LevelChange.
Alignment can be used for a lot of different things,
even if you're just rotating a photo to align a horizon,
for example, you can go ahead and use alignment for that.
LevelChange is what NSButton uses in the multilevel mode;
it will provide haptic feedback as the levels change.
If those two don't sufficiently describe what you're trying
to do, then go ahead and just use generic.
You want your haptic feedback to perform synchronously
with what's going on the screen so that
by default that's the DrawCompleted.
If you're using Cocoa drawing or core animation,
you just use a DrawCompleted performance time
and this will just automatically synchronize them for you
so that you can determine your haptic feedbacks during event
processing and your drawing can just concentrate on drawing.
If you're using Medal or OpenGL directly,
then you can just use Now and as things update on the screen,
you will go ahead and need to make sure that you line them
up so that they occur simultaneously.
That's all there is.
You're now all masters in using the Force Touch trackpad.
I can't wait to see what you do with these things
in your application, we have covered Table Row Actions,
accelerator buttons, Spring Loading, we've talked
about how the force flows through the system,
doing Alignment Feedback, and finally controlling the trackpad
and configuring it for your specific needs.
There's a lot that you can do here.
That little app that I did earlier in my demo,
it's called Force Touch Catalog, you can download
that and check that out.
We also have an alignment guide sample application,
so that's really great to use as well.
I suggest that you also check out the What's New
in Web Development in WebKit and Safari
so you can learn how Safari is exposing pressure
in the web environment.
We have a lab coming up right after this, the Cocoa
and Force Touch and Gesture lab; I will of course be there.
I'm real excited to hear your ideas on how to use Force Touch
in your applications or even to show me anything
that you have already done, I'm really excited to see that
and to talk about it with you.
Please come find me in the lab right after this session.
Thank you very much.
Enjoy the rest of the show.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.