Use the Force Touch
Every once in a while, Mac OS X gains new features that require us to change code. It doesn't happen often. The last time was for retina displays. Now the new-fangled thing is Force Touch. Thus far, the only "feature" that people talk about is that it doesn't "click" when the computer is off. I've literally seen people at the Apple store shut down some of the new macbooks just to ooh and aww over it not clicking when off. This is probably more a factor of force touch feeling almost exactly like the old trackpads. It is absolutely uncanny. There is much much more to it though and with more application support we'll all not know about these hard to discover features. Check out the code and follow along.
The added features were principally to NSEvent which adds member variables for pressure, stage and stageTransition. The Responder chain has a new method called:
- (void)pressureChangeWithEvent:(NSEvent *)event
This method provides a callback for when the pressure does change. It seems to be the best way to easily get pressure anywhere in the responder chain.
Sadly the vibration is a closed thing dealt with only by the OS. I was hoping to have some vibration or feeling for the trackpad. Alas, this will only boost the ability for artists to draw by trackpad. That feels weird to say but every drawing app for mac will soon have this feature and it will be pretty......pretty.....pretty nice.
Now what does it really give us? A float called pressure that goes from 0.0 - 1.0 and a stage which indicates whether it is a regular mouse click type event or a super-click, ahem, I mean force touch. [link]
What happens when you look for pressure and it isn't there? Nothing. It's 0.0f.
Is it tap and tap harder? Ah, kinda, yes. At least it is for the general force pressure in from NSResponder. The pressure goes from 0.0f to 1.0f and then hits the 'force touch' and stage 2 and then goes from 0.0f to 1.0f again.
Ok, now for the demo. I wanted to make a little Mac OS X application that showcases the pressure sensitivity clearly. I'll come back to the NSResponder's version of force touch later in this post.
So first, I want to have a few different representations of the force. We can see the pressureChanges from the NSResponder chain but there are 2 new types of buttons that we can use.
Let's start with the simple NSAcceleratorButton type. It has a new member variable named doubleValue which is not obvious that it is the pressure but then again it is also weird that it gives back a double from 1.0f - 1.99f. In the code, I normalize that to our progress bar's 0-1 value. It only has one stage of pressure and you only feel a click once at the beginning. I've tied mine to a shared progress bar. I should also mention that I was just able to change the button type to Accelerator in Interface builder. This is by far the weirdest implementation of the whole force touch thing. This all makes perfect sense to somebody at Apple.
The second button type of the 'Multi-Level Accelerator' or NSMultiLevelAccelerator which provides multiple levels of pressue with "clicks" (from 1-5 defaulting at 2). The documentation shows that it is 'integerValue' but the doubleValue seems to get the levels too. When set to 5, as you press it keeps "clicking" until you get to 5. It is a cool sensation and I can see a lot of longevity in this, especially for subtle UX decisions (scrubbers, pickers, etc). Again, I normalize the value and send it to our shared progress bar.
Now back to the NSResponder method 'pressureChangeWithEvent:'. Since it is part of NSResponder, it is on almost everything in the UI. I created a NSView subclass called TrackingView and made it yellow. There is a small white circle that shows up when you mouse into the region. That uses one of my favorite Mac OS X features - NSTrackingArea. It allows you to use the methods:
- (void)mouseEntered:(NSEvent *)theEvent
- (void)mouseExited:(NSEvent *)theEvent
- (void)mouseMoved:(NSEvent *)theEvent
This is incredibly helpful for this program so that I can draw a little circle showing the amount of force in your touch.
To achieve this, I am drawing using drawRect: and triggering the drawRect: method with:
With a normal trackpad, pressure just goes from 0 to 1, which is pretty expected to have it work with previous code (the same happens with the multi-level press button too).
The real magic is NSResponder's pressureChangeWithEvent: which is logging the current pressure and it is also calling super so that the parent (NSWindowController) can get those events too.
So check out the code and use Force Touch in your apps to do wild and crazy things. Just be sure to document them so they are discoverable features.
What's next? For this feature to come to iPhones and to make button clicks on the phone feel like real button clicks. It will almost be like...like...a real button. Gosh, haven't we been pining for real buttons on smartphones for a while now?