Leveraging Touch Input on ios

Similar documents
Modern User Interaction on ios

Gestures. Mobile Application Development in ios School of EECS Washington State University Instructor: Larry Holder

Advanced Scrollviews and Touch Handling Techniques

Enhancing your apps for the next dimension of touch

Mastering UIKit on tvos

Quick Interaction Techniques for watchos

Announcements. Lab 2 is due next Monday (Sept 25 th ) by 11:59 PM. Late policy is 10% of lab total per day late. So -7.5 points per day late for lab 2

Implementing UI Designs in Interface Builder

What s New in tvos 12

What s New in tvos #WWDC16. App Frameworks. Session 206. Hans Kim tvos Engineer

Cocoa Touch Best Practices

Extending Your Apps with SiriKit

Media and Gaming Accessibility

Mobile Application Programming. Controls

What's New in UIKit Dynamics and Visual Effects Session 229

Lecture 5 Demo Code: FaceIt MVC and Gestures

Using and Extending the Xcode Source Editor

Mastering Drag and Drop

What s New in imessage Apps

Announcements. Today s Topics

New UIKit Support for International User Interfaces

Advanced Notifications

What s New in watchos

Getting the Most out of Playgrounds in Xcode

What s New in Core Image

ios Accessibility Developing for everyone Session 201 Ian Fisch ios Accessibility

Stanford CS193p. Developing Applications for ios. Winter CS193p! Winter 2015

Building Watch Apps #WWDC15. Featured. Session 108. Neil Desai watchos Engineer

Data Delivery with Drag and Drop

Designing for Apple Watch

Creating Great App Previews

What s New in NSCollectionView Session 225

View Controller Advancements for ios8

Mobile Application Programing: ios. Messaging

What s New in MapKit. App Frameworks #WWDC17. Fredrik Olsson

Event Delivery: The Responder Chain

Integrating Apps and Content with AR Quick Look

Introducing On Demand Resources

Mysteries of Auto Layout, Part 1

Monetize and Promote Your App with iad

Accessibility on ios. Developing for everyone. Frameworks #WWDC14. Session 210 Clare Kasemset ios Accessibility

Developing Applications for ios

Introducing Swift Playgrounds

ios Application Development Lecture 3: Unit 2

Introducing the Modern WebKit API

What s New in Testing

Improving your Existing Apps with Swift

Graphics and Animation

imessage Apps and Stickers, Part 2

Designing Great Apple Watch Experiences

Swift API Design Guidelines

Stanford CS193p. Developing Applications for ios. Winter CS193p. Winter 2017

Stanford CS193p. Developing Applications for ios. Spring CS193p. Spring 2016

Touch Bar Fundamentals

Apple Watch Design Tips and Tricks

What s New in SpriteKit

What s New in Xcode App Signing

Core ML in Depth. System Frameworks #WWDC17. Krishna Sridhar, Core ML Zach Nation, Core ML

lecture 10 UI/UX and Programmatic Design cs : spring 2018

Building Visually Rich User Experiences

Stanford CS193p. Developing Applications for ios. Winter CS193p. Winter 2017

Creating Complications with ClockKit Session 209

App Extension Best Practices

What s New in Core Data?

What s New in Cocoa for macos

Adopting Advanced Features of the New UI

Introducing Password AutoFill for Apps

Stanford CS193p. Developing Applications for ios. Winter CS193p! Winter 2015

Introducing SiriKit. Hey Siri, say hello to apps #WWDC16. App Frameworks. Session 217

What s New in ARKit 2

Introduction to Siri Shortcuts

Enabling Your App for CarPlay

Contents. iphone Training. Industry Trainers. Classroom Training Online Training ON-DEMAND Training. Read what you need

Introducing Metal 2. Graphics and Games #WWDC17. Michal Valient, GPU Software Engineer Richard Schreyer, GPU Software Engineer

Using Grouped Notifications

Creating Content with iad JS

Building Apps with Dynamic Type

Managing Documents In Your ios Apps

ios Mobile Development

Getting the Most Out of HealthKit

Stanford CS193p. Developing Applications for ios. Fall CS193p. Fall

INTRODUCTION TO ARCHITECTING YOUR IOS APP

Announcements. Today s Topics

ios Memory Deep Dive #WWDC18 Kyle Howarth, Software Engineer James Snee, Software Engineer Kris Markel, Software Engineer

Building Better Apps with Value Types in Swift Session 414

WatchKit In-Depth, Part 2

Views, Drawing, and Events. Lecture 5

The Design and Implementation of a Lightweight Game Engine for the iphone Platform

Stanford CS193p. Developing Applications for ios. Spring CS193p. Spring 2016

Optimizing Swift Performance Session 409

What s New in Core Bluetooth

Building Faster in Xcode

What s New in Metal, Part 2

Mobile Apps Introduction Getting Started Features Resources

What s New in Foundation for Swift Session 207

ITP 342 Mobile App Dev. Connections

CarPlay Audio and Navigation Apps

Advances in AVFoundation Playback

Storyboards and Controllers on OS X

ios 11 App Development Essentials

Transcription:

App Frameworks #WWDC16 Leveraging Touch Input on ios And getting the most out of Apple Pencil Session 220 Dominik Wagner UIKit Engineer 2016 Apple Inc. All rights reserved. Redistribution or public display not permitted without written permission from Apple.

New and Recent Hardware

3D Touch iphone 6s and iphone 6s Plus A Peek at 3D Touch Presidio Thursday 4:00PM

Faster Touch Scanning ipad Air 2 and ipad Pro

Apple Pencil ipad Pro Precise location 240 Hz scan rate Tilt, orientation, and force Palm rejection

Siri Remote Apple TV UIFocusEngine Game Controller Indirect touches

Siri Remote Apple TV UIFocusEngine Game Controller Indirect touches Apple TV Tech Talks Controlling Game Input for Apple TV Mission Wednesday 5:00PM

Building a Drawing App

Building a Drawing App Agenda New API Step-by-step Sample code available

Say Hello to SpeedSketch One sheet of paper you can draw on Full support for Apple Pencil and 3D Touch

Building a Drawing App Model and capture

Building a Drawing App Model and capture Series of strokes UITouch in event callbacks Copy the relevant data

Building a Drawing App Model and capture struct StrokeSample { let location: CGPoint StrokeSample

Building a Drawing App Model and capture class Stroke { var samples: [StrokeSample] = [] Stroke StrokeSample func add(sample: StrokeSample) StrokeSample StrokeSample

Building a Drawing App Model and capture class Stroke { var samples: [StrokeSample] = [] var state: StrokeState =.active func add(sample: StrokeSample) Stroke StrokeSample StrokeSample StrokeSample enum StrokeState { case active case done case cancelled

Building a Drawing App Model and capture class StrokeCollection { var strokes: [Stroke] = [] StrokeCollection Stroke func add(donestroke: stroke) StrokeSample StrokeSample StrokeSample Stroke Stroke

Building a Drawing App Model and capture class StrokeCollection { StrokeCollection var strokes: [Stroke] = [] var activestroke: Stroke? func add(donestroke: stroke) Stroke StrokeSample StrokeSample StrokeSample Stroke Stroke

Building a Drawing App Model and capture Where to capture? UIGestureRecognizer UIView Up the responder chain

Building a Drawing App Capture Custom UIGestureRecognizer Stroke Gesture Recognizer Targeting the main view controller View controller facilitates update Canvas View Controller Stroke View

import UIKit.UIGestureRecognizerSubclass class StrokeGestureRecognizer: UIGestureRecognizer {

import UIKit.UIGestureRecognizerSubclass class StrokeGestureRecognizer: UIGestureRecognizer { var stroke = Stroke()

import UIKit.UIGestureRecognizerSubclass class StrokeGestureRecognizer: UIGestureRecognizer { var stroke = Stroke() override func touchesbegan(_ touches: Set<UITouch>, with event: UIEvent?) {

import UIKit.UIGestureRecognizerSubclass class StrokeGestureRecognizer: UIGestureRecognizer { var stroke = Stroke() func appendtouches(_ touches: Set<UITouch>, event: UIEvent?) -> Bool override func touchesbegan(_ touches: Set<UITouch>, with event: UIEvent?) {

import UIKit.UIGestureRecognizerSubclass class StrokeGestureRecognizer: UIGestureRecognizer { var stroke = Stroke() func appendtouches(_ touches: Set<UITouch>, event: UIEvent?) -> Bool override func touchesbegan(_ touches: Set<UITouch>, with event: UIEvent?) { if appendtouches(touches, event:event) { state =.began

import UIKit.UIGestureRecognizerSubclass class StrokeGestureRecognizer: UIGestureRecognizer { var stroke = Stroke() func appendtouches(_ touches: Set<UITouch>, event: UIEvent?) -> Bool override func touchesbegan(_ touches: Set<UITouch>, with event: UIEvent?) { if appendtouches(touches, event:event) { state =.began override func touchesmoved(_ touches: Set<UITouch>, with event: UIEvent?) { if appendtouches(touches, event:event) { state =.changed

import UIKit.UIGestureRecognizerSubclass class StrokeGestureRecognizer: UIGestureRecognizer { var stroke = Stroke() func appendtouches(_ touches: Set<UITouch>, event: UIEvent?) -> Bool override func touchesbegan(_ touches: Set<UITouch>, with event: UIEvent?) { if appendtouches(touches, event:event) { state =.began override func touchesmoved(_ touches: Set<UITouch>, with event: UIEvent?) { if appendtouches(touches, event:event) { state =.changed override func touchesended(_ touches: Set<UITouch>, with event: UIEvent?) override func touchescancelled(_ touches: Set<UITouch>, with event: UIEvent?)

import UIKit.UIGestureRecognizerSubclass class StrokeGestureRecognizer: UIGestureRecognizer { var stroke = Stroke() func appendtouches(_ touches: Set<UITouch>, event: UIEvent?) -> Bool override func touchesbegan(_ touches: Set<UITouch>, with event: UIEvent?) { if appendtouches(touches, event:event) { state =.began override func touchesmoved(_ touches: Set<UITouch>, with event: UIEvent?) { if appendtouches(touches, event:event) { state =.changed override func touchesended(_ touches: Set<UITouch>, with event: UIEvent?) override func touchescancelled(_ touches: Set<UITouch>, with event: UIEvent?) override func reset() { stroke = Stroke() super.reset()

import UIKit.UIGestureRecognizerSubclass class StrokeGestureRecognizer: UIGestureRecognizer { var stroke = Stroke() func appendtouches(_ touches: Set<UITouch>, event: UIEvent?) -> Bool override func touchesbegan(_ touches: Set<UITouch>, with event: UIEvent?) { if appendtouches(touches, event:event) { state =.began override func touchesmoved(_ touches: Set<UITouch>, with event: UIEvent?) { if appendtouches(touches, event:event) { state =.changed override func touchesended(_ touches: Set<UITouch>, with event: UIEvent?) override func touchescancelled(_ touches: Set<UITouch>, with event: UIEvent?) override func reset() { stroke = Stroke() super.reset()

class CanvasViewController: UIViewController { override func viewdidload() { super.viewdidload()

class CanvasViewController: UIViewController { override func viewdidload() { super.viewdidload() let strokerecognizer = StrokeGestureRecognizer( target: self, action: #selector(strokeupdated(_:)) ) view.addgesturerecognizer(strokerecognizer)

class CanvasViewController: UIViewController { override func viewdidload() { super.viewdidload() let strokerecognizer = StrokeGestureRecognizer( target: self, action: #selector(strokeupdated(_:)) ) view.addgesturerecognizer(strokerecognizer) func strokeupdated(_ strokegesture: StrokeGestureRecognizer) { view.stroketodraw = strokegesture.stroke

class CanvasViewController: UIViewController { override func viewdidload() { super.viewdidload() let strokerecognizer = StrokeGestureRecognizer( target: self, action: #selector(strokeupdated(_:)) ) view.addgesturerecognizer(strokerecognizer) func strokeupdated(_ strokegesture: StrokeGestureRecognizer) { view.stroketodraw = strokegesture.stroke

Let s have a look

Pencil tip

Pencil Excessive gap Long, choppy lines

Analysis of First Attempt Missed events Drawing engine Did not use the new ios 9.0 API

Anatomy of a Stroke

Anatomy of a Stroke

Anatomy of a Stroke Ended Began

Anatomy of a Stroke Ended Began

Anatomy of a Stroke Ended Moved Moved Moved Moved Moved Began

Anatomy of a Stroke Ended Moved Moved Moved Moved Moved Began

Anatomy of a Stroke Coalesced Ended Moved Moved Moved Moved Moved Began

Anatomy of a Stroke Coalesced Ended Moved Moved Moved Moved Moved Began

Anatomy of a Stroke Coalesced Ended Moved Moved Moved Moved Moved Began

Coalesced Touches class UIEvent { public func coalescedtouches(for touch: UITouch) -> [UITouch]?

Coalesced Touches class StrokeGestureRecognizer: UIGestureRecognizer { override func touchesmoved(_ touches: Set<UITouch>, with event: UIEvent?) { if let touch = touches.first { appendtouch(touch)

Coalesced Touches class StrokeGestureRecognizer: UIGestureRecognizer { override func touchesmoved(_ touches: Set<UITouch>, with event: UIEvent?) { if let touch = touches.first { for coalescedtouch in event.coalescedtouches(for: touch) { appendtouch(touch)

Pencil Coalesced touches (gray) Live touch (black) Excessive gap Too many coalesced touches

Analysis of Second Attempt Speed is still lacking UIKit helped us by coalescing

Do Not Draw on Every Touch Event Display refresh rate is 60 Hz Incoming event frequency can reach 240 Hz and more Do not try to draw faster than screen can refresh

When to Render? UIView Use setneedsdisplay() Implement draw(_ rect: CGRect) GLKView, MTLView Set the enablesetneedsdisplay property to true If required, draw at a steady rate using a CADisplayLink object

class StrokeCGView: UIView { var stroketodraw: Stroke? { didset { drawimageandupdate()

class StrokeCGView: UIView { var stroketodraw: Stroke? { didset { setneedsdisplay()

Even Better Mark only the changed areas setneedsdisplayin(rect: changedrect) Activate drawsasynchronously on the layer class StrokeCGView: UIView { override init(frame: CGRect) { super.init(frame: frame) layer.drawsasynchronously = true

Steady amount of coalesced touches Still some lag

Improve Perceived Latency Use predicted touches class UIEvent { public func predictedtouches(for touch: UITouch) -> [UITouch]?

Predicted Touches Add predicted touches to your data structure temporarily Choose their appearance, depending on your app To appear like actual touches To appear as tentative

Predicted Touches class StrokeGestureRecognizer: UIGestureRecognizer { override func touchesmoved(_ touches: Set<UITouch>, with event: UIEvent?) { if let touch = touches.first { for coalescedtouch in event.coalescedtouches(for:touch) { appendtouch(touch)

Predicted Touches class StrokeGestureRecognizer: UIGestureRecognizer { override func touchesmoved(_ touches: Set<UITouch>, with event: UIEvent?) { if let touch = touches.first { for coalescedtouch in event.coalescedtouches(for:touch) { appendtouch(touch) for predictedtouch in event.predictedtouches(for:touch) { appendtouchtemporarily(touch)

Predicted Touches class StrokeGestureRecognizer: UIGestureRecognizer { override func touchesmoved(_ touches: Set<UITouch>, with event: UIEvent?) { if let touch = touches.first { cleartemporarytouches() for coalescedtouch in event.coalescedtouches(for:touch) { appendtouch(touch) for predictedtouch in event.predictedtouches(for:touch) { appendtouchtemporarily(touch)

Predicted touches

What Have We Seen so Far Collect input data using a UIGestureRecognizer Access coalesced touches Make rendering fast and efficient Use predicted touches

Apple Pencil

Apple Pencil Touch types public var type: UITouchType { get

Apple Pencil Touch types public var type: UITouchType { get enum UITouchType : Int { case direct case indirect case stylus

Apple Pencil Higher precision func preciselocation(in view: UIView?) -> CGPoint func precisepreviouslocation(in view: UIView?) -> CGPoint

Apple Pencil and 3D Touch Force public var force: CGFloat { get public var maximumpossibleforce: CGFloat { get Device Range iphone 6s (Plus) 0.0 - maximumpossibleforce Apple Pencil on ipad Pro 0.0 - maximumpossibleforce Touch on ipad Pro and all previous devices 0.0

Apple Pencil and 3D Touch Force func touchesbegan(_ touches: Set<UITouch>, with event: UIEvent) func touchesmoved(_ touches: Set<UITouch>, with event: UIEvent) func touchesended(_ touches: Set<UITouch>, with event: UIEvent) func touchescancelled(_ touches: Set<UITouch>, with event: UIEvent)

Apple Pencil and 3D Touch Tap recognition func touchesmoved(_ touches: Set<UITouch>, with event: UIEvent) { canceltap() Use UITapGestureRecognizer

Force Add to the model struct StrokeSample { let location: CGPoint

Force Add to the model struct StrokeSample { let location: CGPoint var force: CGFloat?

Width based on force

Apple Pencil Tilt

Apple Pencil Tilt Altitude

Apple Pencil Tilt var altitudeangle: CGFloat { get

Apple Pencil Orientation

Apple Pencil Orientation

Apple Pencil Orientation

Apple Pencil Orientation

Apple Pencil Orientation Azimuth

Apple Pencil Azimuth func azimuthangle(in view: UIView?) -> CGFloat func azimuthunitvector(in view: UIView?) -> CGVector

Apple Pencil Force Along axis

Apple Pencil Force Perpendicular to device surface

Apple Pencil Force var perpendicularforce: CGFloat { get { return force / sin(altitudeangle)

Apple Pencil Force var perpendicularforce: CGFloat { get { return min( force / sin(altitudeangle), maximumpossibleforce)

Apple Pencil Force

Apple Pencil Estimated properties var estimatedproperties: UITouchProperties { get

Apple Pencil Estimated properties var estimatedproperties: UITouchProperties { get struct UITouchProperties : OptionSet {

Apple Pencil Estimated properties var estimatedproperties: UITouchProperties { get struct UITouchProperties : OptionSet { static var force: UITouchProperties { get

Apple Pencil Estimated properties var estimatedproperties: UITouchProperties { get struct UITouchProperties : OptionSet { static var force: UITouchProperties { get static var azimuth: UITouchProperties { get static var altitude: UITouchProperties { get

Apple Pencil Estimated properties var estimatedproperties: UITouchProperties { get struct UITouchProperties : OptionSet { static var force: UITouchProperties { get static var azimuth: UITouchProperties { get static var altitude: UITouchProperties { get static var location: UITouchProperties { get

Apple Pencil Estimated properties var estimatedproperties: UITouchProperties { get struct UITouchProperties : OptionSet { static var force: UITouchProperties { get static var azimuth: UITouchProperties { get static var altitude: UITouchProperties { get static var location: UITouchProperties { get var estimatedpropertiesexpectingupdates: UITouchProperties { get

Apple Pencil Estimated properties with updates func touchesbegan(_ touches: Set<UITouch>, with event: UIEvent) func touchesmoved(_ touches: Set<UITouch>, with event: UIEvent) func touchesended(_ touches: Set<UITouch>, with event: UIEvent) func touchescancelled(_ touches: Set<UITouch>, with event: UIEvent)

Apple Pencil Estimated properties with updates func touchesbegan(_ touches: Set<UITouch>, with event: UIEvent) func touchesmoved(_ touches: Set<UITouch>, with event: UIEvent) func touchesended(_ touches: Set<UITouch>, with event: UIEvent) func touchescancelled(_ touches: Set<UITouch>, with event: UIEvent) func touchesestimatedpropertiesupdated(_ touches: Set<UITouch>)

Apple Pencil Estimated properties with updates Check estimatedpropertiesexpectingupdates Use estimationupdateindex as key to index your sample Look up the estimationupdateindex in touchesestimatedpropertiesupdated(_:) Some updates will arrive after touchesended:

Apple Pencil Estimated properties with updates override func touchesestimatedpropertiesupdated(_ touches: Set<UITouch>) { for touch in touches {

Apple Pencil Estimated properties with updates override func touchesestimatedpropertiesupdated(_ touches: Set<UITouch>) { for touch in touches { let estimationindex = touch.estimationupdateindex!

Apple Pencil Estimated properties with updates override func touchesestimatedpropertiesupdated(_ touches: Set<UITouch>) { for touch in touches { let estimationindex = touch.estimationupdateindex! let (sample, sampleindex) = samplesexpectingupdates[estimationindex]

Apple Pencil Estimated properties with updates override func touchesestimatedpropertiesupdated(_ touches: Set<UITouch>) { for touch in touches { let estimationindex = touch.estimationupdateindex! let (sample, sampleindex) = samplesexpectingupdates[estimationindex] let updatedsample = updatedsample(with: touch)

Apple Pencil Estimated properties with updates override func touchesestimatedpropertiesupdated(_ touches: Set<UITouch>) { for touch in touches { let estimationindex = touch.estimationupdateindex! let (sample, sampleindex) = samplesexpectingupdates[estimationindex] let updatedsample = updatedsample(with: touch) stroke.update(sample: updatedsample, at: sampleindex)

Apple Pencil Estimated properties with updates override func touchesestimatedpropertiesupdated(_ touches: Set<UITouch>) { for touch in touches { let estimationindex = touch.estimationupdateindex! let (sample, sampleindex) = samplesexpectingupdates[estimationindex] let updatedsample = updatedsample(with: touch) stroke.update(sample: updatedsample, at: sampleindex) if touch.estimatedpropertiesexpectingupdates == [] { samplesexpectingupdates.removevalue(forkey: sampleindex)

Azimuth

Finishing Touches Canvas 84

Finishing Touches Canvas 84

Finishing Touches Canvas 84

Finishing Touches Adjusting gestures Scroll view UIPanGestureRecognizer vs. StrokeGestureRecognizer Disable scrolling with Apple Pencil class UIGestureRecognizer { public var allowedtouchtypes: [NSNumber] let pan = scrollview.pangesturerecognizer pan.allowedtouchtypes = [UITouchType.direct.rawValue as NSNumber] strokerecognizer.allowedtouchtypes = [UITouchType.stylus.rawValue as NSNumber]

Finishing Touches Adjusting gestures class UIGestureRecognizer { public var requiresexclusivetouchtype: Bool

Summary New properties of UITouch Coalesced and predicted touches Property estimation Adjusting gestures

1024 x 768

1024 x 768

More Information https://developer.apple.com/wwdc16/220

Related Sessions Controlling Game Input for Apple TV Mission Wednesday 5:00PM A Peek at 3D Touch Presidio Thursday 4:00PM Advanced Touch Input on ios WWDC 2015

Labs UIKit and UIKit Animations Lab Cocoa Touch and 3D Touch Lab Frameworks Lab C Frameworks Lab C Thursday 1:00PM Friday 10:30AM