Slide 1

Slide 1 text

3D TOUCH An introduction to 3D Touch for Swift developers Simon Gladman for Swift London / October 2015

Slide 2

Slide 2 text

HELLO FLEX MONKEY • Blog: flexmonkey.blogspot.co.uk • GitHub: github.com/FlexMonkey • Twitter: @FlexMonkey

Slide 3

Slide 3 text

3D TOUCH FOR USERS

Slide 4

Slide 4 text

3D TOUCH FOR USERS • Peek

Slide 5

Slide 5 text

3D TOUCH FOR USERS • Peek • Preview Actions

Slide 6

Slide 6 text

3D TOUCH FOR USERS • Peek • Preview Actions • Pop

Slide 7

Slide 7 text

3D TOUCH FOR USERS • Peek • Preview Actions • Pop • Quick Actions

Slide 8

Slide 8 text

3D TOUCH FOR USERS • Peek • Preview Actions • Pop • Quick Actions • Pressure Sensitivity

Slide 9

Slide 9 text

3D TOUCH FOR DEVS • Simple API • Check device supports 3D Touch traitCollection.forceTouchCapability == UIForceTouchCapability.Available

Slide 10

Slide 10 text

• Simple API • Getting force value override func touchesBegan(touches: Set, withEvent event: UIEvent?) { guard let touch = touches.first else { return } let normalisedForce = touch.force / touch.maximumPossibleForce print(normalisedForce) }

Slide 11

Slide 11 text

• Simple API • Implementing peek and pop registerForPreviewingWithDelegate(delegate, sourceView: sourceView) • sourceView reacts to deep press • delegate must implement UIViewControllerPreviewingDelegate

Slide 12

Slide 12 text

• Peeking & Popping with Previewing Delegate • Protocol contains func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController?

Slide 13

Slide 13 text

• Peeking & Popping with Previewing Delegate • Protocol contains func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController)

Slide 14

Slide 14 text

3D TOUCH: PEEKING • Peeking - a deep press func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController?

Slide 15

Slide 15 text

• Peeking func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? { let peek = PeekViewController(hsl: hsl, delegate: previewingContext.delegate) return peek }

Slide 16

Slide 16 text

• Peeking let hsl: HSL unowned let delegate: UIViewControllerPreviewingDelegate required init(hsl: HSL, delegate: UIViewControllerPreviewingDelegate) { self.hsl = hsl self.delegate = delegate super.init(nibName: nil, bundle: nil) let color = UIColor(hue: hsl.hue, saturation: hsl.saturation, brightness: hsl.lightness, alpha: 1) label.text = "Your color: \(color.getHex())" label.textAlignment = NSTextAlignment.Center smallSwatch.backgroundColor = color }

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

3D TOUCH: PREVIEW ACTIONS • Preview Actions • The previewing delegate simply needs to implement previewActions()

Slide 19

Slide 19 text

• Preview Actions enum ColorPresets: String { case Red, Green, Blue } var previewActions: [UIPreviewActionItem] { return [ColorPresets.Red, ColorPresets.Green, ColorPresets.Blue].map { UIPreviewAction(title: $0.rawValue, style: UIPreviewActionStyle.Default, handler: { (previewAction, viewController) in (viewController as? PeekViewController)?.updateColor(previewAction) }) } }

Slide 20

Slide 20 text

• Preview Actions func updateColor(previewAction: UIPreviewActionItem) { guard let delegate = delegate as? ChromaTouchViewController, preset = ColorPresets(rawValue: previewAction.title) else { return } let hue: CGFloat switch preset { case .Blue: hue = 0.667 case .Green: hue = 0.333 case .Red: hue = 0 } delegate.hsl = HSL(hue: hue, saturation: 1, lightness: 1) }

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

3D TOUCH: POPPING • Popping - a deeper press func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController)

Slide 23

Slide 23 text

• Popping func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) { swatch.userInteractionEnabled = false hslSlidersStackView.hidden = true }

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

3D TOUCH: PRESSURE SENSITIVITY • Touch gestures have ‘x’ and ‘y’ coordinates • Now have a ‘z’ coordinate corresponding to the pressure of the touch

Slide 26

Slide 26 text

• Override touchesMoved(): override func touchesMoved(touches: Set, withEvent event: UIEvent?) { guard let touch = touches.first else { return } let touchLocation = touch.locationInView(self) let force = touch.force let maximumPossibleForce = touch.maximumPossibleForce let normalisedXPosition = touchLocation.x / frame.width let normalisedYPosition = touchLocation.y / frame.height let normalisedZPosition = force / maximumPossibleForce hsl = HSL(hue: normalisedXPosition, saturation: normalisedYPosition, lightness: normalisedZPosition) sendActionsForControlEvents(.ValueChanged) }

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

GESTURE RECOGNISERS • As an alternative to overriding a control’s touch handling methods • By extending UIGestureRecognizer let deepPressGestureRecognizer = DeepPressGestureRecognizer(target: self, action: "deepPressHandler:", threshold: 0.75) button.addGestureRecognizer(deepPressGestureRecognizer)

Slide 29

Slide 29 text

• Handle user’s actions in gesture recogniser’s touch handlers override func touchesMoved(touches: Set, withEvent event: UIEvent) { guard let view = view, touch = touches.first where touch.force != 0 && touch.maximumPossibleForce != 0 else { return } if !deepPressed && (touch.force / touch.maximumPossibleForce) >= threshold { state = UIGestureRecognizerState.Began deepPressed = true } else if deepPressed && (touch.force / touch.maximumPossibleForce) < threshold { state = UIGestureRecognizerState.Ended deepPressed = false } }

Slide 30

Slide 30 text

TOUCH COALESCING • touchesMoved() samples at up to 60 hertz

Slide 31

Slide 31 text

TOUCH COALESCING • touchesMoved() samples at up to 60 hertz • iPhone 6s has a touch sample rate of 120 hertz

Slide 32

Slide 32 text

TOUCH COALESCING • touchesMoved() samples at up to 60 hertz • iPhone 6s has a touch sample rate of 120 hertz • iPad Pro will sample touches at 240 hertz

Slide 33

Slide 33 text

• The missing touches can be accessed through the event’s coalescedTouches override func touchesMoved(touches: Set, withEvent event: UIEvent?) { super.touchesMoved(touches, withEvent: event) guard let touch = touches.first, coalescedTouches = event?.coalescedTouchesForTouch(touch) else { return } for coalescedTouch in coalescedTouches { let locationInView = coalescedTouch.locationInView(view) print(locationInView) } }

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

SOME DEMO APPS

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

http://goo.gl/rzPExI flexmonkey.blogspot.co.uk github.com/FlexMonkey