Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Advanced Touch Handling in iOS9: Coalescing and Prediction

DZone's Guide to

Advanced Touch Handling in iOS9: Coalescing and Prediction

Recent iOS devices sense touch at 120Hz, but out-of-the-box handling configuration means you could miss half the user’s inputs.

· Mobile Zone
Free Resource

Launching an app doesn’t need to be daunting. Whether you’re just getting started or need a refresher on mobile app testing best practices, this guide is your resource! Brought to you in partnership with Perfecto

Luckily, with iOS 9, Apple has introduced some new functionality to UIEvent to reduce touch latency and improve temporal resolution. There's a demo application to go with this post that you can find at my GitHub repository here.

Touch Coalescing

The first feature is touch coalescing. If your app is sampling touches with an overridden touchesMoved(), the most amount of touch samples you’ll get is 60 per second and, if your app is concerning itself with some fancy calculations on the main thread, it could well be less than that.

Touch coalescing allows you to access all the intermediate touches that may have occurred between touchesMoved() invocations.  This allows, for example, your app to draw a smooth curve consisting of half a dozen points when you’ve only received one UIEvent.

The syntax is super simple. In my demo application, I have a few CALayers that I draw upon. The first layer (mainDrawLayer, which contains chubby blue lines) is drawn on using information from the mainUIEvent from touchesMoved():

 override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
    {
        super.touchesMoved(touches, withEvent: event)

        guard let touch = touches.first, event = event else

        {
            return
        }
        mainDrawPath.addLineToPoint(touch.locationInView(view))

        mainDrawLayer.path = mainDrawPath.CGPath

        [...]

If the user moves their finger across the screen fast enough, they’ll get a jagged line, which is probably not what they want. To access the intermediate touches, I use the coalescedTouchesForTouch method of the event which returns an array of UITouch:

[...]
        if let coalescedTouches = event.coalescedTouchesForTouch(touch)
        {
            print("coalescedTouches:", coalescedTouches.count)           
            for coalescedTouch in coalescedTouches
            {
                coalescedDrawPath.addLineToPoint(coalescedTouch.locationInView(view))
            }          
            coalescedDrawLayer.path = coalescedDrawPath.CGPath
        }
[...]

Here, I loop over those touches (I trace the number of touches to the console for information) and create a thinner yellow line by drawing oncoalescedDrawLayer as an overlay.  I’ve added a horrible loop to the touchesMoved method to slow things down a bit:

        var foo = Double(1)    
        for bar in 0 ... 1_000_000
        {
            foo += sqrt(Double(bar))
        }

If you run this app on your iOS 9 device and scribble away you can see the two results: a jagged thick blue line and a beautifully smooth thinner yellow line based on all those intermediate touch events that could have been missed.

Predictive Touch

Something maybe even cleverer is touch prediction which allows you to preempt where a user’s finger (or Apple Pencil) may be in the near future. Predictive touch uses some highly tuned algorithms and is continually updated with where iOS expects the users touch to be in approximately a frame in the future. This means you could begin preparing user interface components (e.g. begin a fade in) before they’re actually required to help reduce latency.

In my demo application, I display the predicted touch as a grey circle. The syntax is not dissimilar to that of coalesced touch: the event has a new method, predictedTouchesForTouch(), which returns an an array of UITouch:

[...]
        if let predictedTouches = event.predictedTouchesForTouch(touch)
        {
            print("predictedTouches:", predictedTouches.count)            
            for predictedTouch in predictedTouches
            {
                let locationInView =  predictedTouch.locationInView(view)                
                let circle = UIBezierPath(ovalInRect: CGRect(x: locationInView.x - 4, y: locationInView.y - 4, width: 8, height: 8))                
                predictedDrawPath.appendPath(circle)
            }            
            predictedDrawLayer.path = predictedDrawPath.CGPath
        }
[...]

You can see a few grey circles in the screen grab above which show two touches iOS predicted I would do to complete the spiral - that I never actually did! Spooky!

Conclusion

As users demand less latency and a higher touch resolution, touch coalescing and prediction allow our apps to support that. Whatever the framerate of our apps, we can respond to all of the user’s gestures and even preempt some of them!

The source code for this demo app is available at my GitHub repository here.

Keep up with the latest DevTest Jargon with the latest Mobile DevTest Dictionary. Brought to you in partnership with Perfecto.

Topics:
ios 9 feature ,mobile app development ,objective c

Published at DZone with permission of Simon Gladman, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}