Slide 1

Slide 1 text

Taming Animations

Slide 2

Slide 2 text

Animations • Breathtaking • Easy to build • Easy to control

Slide 3

Slide 3 text

Animations1 • Breathtaking • Easy to build • Easy to control 1 Chose two

Slide 4

Slide 4 text

Why it's important to control animations? • Sometimes it's not • Software engineers are control freaks • Designers will ❤ you

Slide 5

Slide 5 text

Why to control animations? (srlsy) • Make app feel responsive

Slide 6

Slide 6 text

Why to control animations? (srlsy) • Make app feel responsive

Slide 7

Slide 7 text

Why to control animations? (srlsy) • Make app feel responsive • Creating immersive UI interactions

Slide 8

Slide 8 text

Agenda • UIView.animate(with… • UIViewPropertyAnimator • CoreAnimation • UIDynamics • POP • SceneKit • Keyframes, Lottie

Slide 9

Slide 9 text

Legend complexity ! -----> " control ! -----> " flexibility ! -----> "

Slide 10

Slide 10 text

UIView.animate(with…

Slide 11

Slide 11 text

UIView.animate(with… ! • Easy to use2 UIView.animate(withDuration: 2) { myView.center = CGPoint(x: 300, y: 150) } 2 Mandatory reference to "Simple Made Easy" https://www.infoq.com/presentations/Simple- Made-Easy

Slide 12

Slide 12 text

UIView.animate(with… ! • Easy to use2 • No learning curve (maybe .easeIn !) 2 Mandatory reference to "Simple Made Easy" https://www.infoq.com/presentations/Simple- Made-Easy

Slide 13

Slide 13 text

UIView.animate(with… ! • Easy to use2 • No learning curve (maybe .easeIn !) • Powered by CoreAnimation: out of process rendering, won't compete for main thread resources 2 Mandatory reference to "Simple Made Easy" https://www.infoq.com/presentations/Simple- Made-Easy

Slide 14

Slide 14 text

UIView.animate(with… ! • Not all properties can be animated UIView.animate(withDuration: 2) { myView.layer.cornerRadius = 20 // iOS 11 ! myShapeView.shapeLayer.path = starPath // " }

Slide 15

Slide 15 text

UIView.animate(with… ! • Not all properties can be animated • No designated way to interrupt: func setEnabled(_ enabled: Bool) { UIView.animate(withDuration: 2, delay: 0, options: [.beginFromCurrentState, .allowUserInteraction]) animations: { self.backgroundColor = enabled ? .blue : .grey }, completion: nil) }

Slide 16

Slide 16 text

UIView.animate(with… ! • Not all properties can be animated • No designated way to interrupt • Can't be controlled func onSliderChange(_ sender: UISlider) { // ??? }

Slide 17

Slide 17 text

UIView.animate(with… ! • Not all properties can be animated • No designated way to interrupt • Can't be controlled • Chaining via nested blocks hell UIView.animate(withDuration: 3, animation: { self.backgroundColor = .red }, completion: { })

Slide 18

Slide 18 text

UIView.animate(with… ! • Not all properties can be animated • No designated way to interrupt • Can't be controlled • Chaining via nested blocks hell UIView.animate(withDuration: 3, animation: { self.backgroundColor = .red }, completion: { UIView.animate(withDuration: 3, animation: { self.alpha = 0 }, completion: { }); })

Slide 19

Slide 19 text

UIView.animate(with… ! • Not all properties can be animated • No designated way to interrupt • Can't be controlled • Chaining via nested blocks hell UIView.animate(withDuration: 3, animation: { self.backgroundColor = .red }, completion: { UIView.animate(withDuration: 3, animation: { self.alpha = 0 }, completion: { UIView.animate(withDuration: 3, animations:{ self.fire.setOn(); } completion:{ }); }); })

Slide 20

Slide 20 text

UIView.animate(with… ! • Not all properties can be animated • No designated way to interrupt • Can't be controlled • Chaining via nested blocks hell UIView.animate(withDuration: 3, animation: { self.backgroundColor = .red }, completion: { UIView.animate(withDuration: 3, animation: { self.alpha = 0 }, completion: { UIView.animate(withDuration: 3, animations:{ self.fire.setOn(); } completion:{ UIView.animate(withDuration: 3, animation: { self.backgroundColor = .red }, completion: { UIView.animate(withDuration: 3, animation: { self.backgroundColor = .red }, completion: { }) }) }); }); })

Slide 21

Slide 21 text

UIView.animate(with… ! • Fire and forget • Small UI animations that should never be interrupted • If you are lazy

Slide 22

Slide 22 text

UIView.animate(with… ! • Fire and forget • Small UI animations that should never be interrupted • If you are lazy If it's enough

Slide 23

Slide 23 text

UIView.animate(with… complexity ! ×----> " control ! ×----> " flexibility ! -×---> "

Slide 24

Slide 24 text

UIViewPropertyAnimator

Slide 25

Slide 25 text

UIViewPropertyAnimator ! • Retains simplicity of block-based API while gives more control animator = UIViewPropertyAnimator(duration: 2, curve: .easeInOut) { myView.center = CGPoint(x: 300, y: 150) }

Slide 26

Slide 26 text

UIViewPropertyAnimator ! • Retains simplicity of block-based API while gives more control • Add more elements to animation animator.addAnimation { myView.opacity = 0 }

Slide 27

Slide 27 text

UIViewPropertyAnimator ! • Retains simplicity of block-based API while gives more control • Add more elements to animation • Poor man chaining animator.addAnimation({ // and then fade out myView.opacity = 0 }, delayFactor: 1)

Slide 28

Slide 28 text

UIViewPropertyAnimator ! • Retains simplicity of block-based API while gives more control • Add more elements to animation • Poor man chaining • Scrub through animation animator.fractionComplete = slider.value

Slide 29

Slide 29 text

UIViewPropertyAnimator ! • Retains simplicity of block-based API while gives more control • Add more elements to animation • Poor man chaining • Scrub through animation • Resume paused animation (after scrubbing) animator.startAnimation()

Slide 30

Slide 30 text

UIViewPropertyAnimator ! • Still relies on properties being exposed to UIKit

Slide 31

Slide 31 text

UIViewPropertyAnimator ! • Still relies on properties being exposed to UIKit • Making animation interactive messes following properties setup animator.addAnimation { myView.center = CGPoint(x: 300, y: 150) } animator.pauseAnimation() // this is unlikely to work correctly... myView.center = CGPoint(x: 50, y: 150)

Slide 32

Slide 32 text

UIViewPropertyAnimator ! • Still relies on properties being exposed to UIKit • Making animation interactive messes following properties setup • Making animation interactive kills completion, need to observe isRunning animator.pausesOnCompletion = true animator.addCompletion { _ in // never called ! }

Slide 33

Slide 33 text

UIViewPropertyAnimator ! • Still relies on properties being exposed to UIKit • Making animation interactive messes following properties setup • Making animation interactive kills completion, need to observe isRunning • Deallocating paused animator explodes deinit { animator.stopAnimation(true) }

Slide 34

Slide 34 text

UIViewPropertyAnimator ! • Still relies on properties being exposed to UIKit • Making animation interactive messes following properties setup • Making animation interactive kills completion, need to observe isRunning • Deallocating paused animator explodes • Composition via coordinating array of animators3 3 Wanna project with 5k stars? Build composable UIViewPropertyAnimator subclass.

Slide 35

Slide 35 text

UIViewPropertyAnimator ! • Great for simple to somewhat complex animations4 • Good in most of the cases unless you find otherwise • Orchestrating complex animations might be hard 4 Advanced Animations with UIKit https://developer.apple.com/videos/play/wwdc2017/230/

Slide 36

Slide 36 text

UIViewPropertyAnimator complexity ! ×----> " control ! -×---> " flexibility ! -×---> "

Slide 37

Slide 37 text

CoreAnimation

Slide 38

Slide 38 text

CoreAnimation ! • Allows to animate everything. Yes even that!

Slide 39

Slide 39 text

CoreAnimation ! • Allows to animate everything. Yes even that! • Out of process rendering - super fast. You can enroll custom callback into same pipeline5 5 https://www.objc.io/issues/12-animations/animating-custom-layer-properties/

Slide 40

Slide 40 text

CoreAnimation ! • Allows to animate everything. Yes even that! • Out of process rendering - super fast. You can enroll custom callback into same pipeline5 • Chaining, delays, relative time… oh my! 5 https://www.objc.io/issues/12-animations/animating-custom-layer-properties/

Slide 41

Slide 41 text

CoreAnimation ! • Allows to animate everything. Yes even that! • Out of process rendering - super fast. You can enroll custom callback into same pipeline5 • Chaining, delays, relative time… oh my! • Scrubbing using speed = 0 & timeOffset 5 https://www.objc.io/issues/12-animations/animating-custom-layer-properties/

Slide 42

Slide 42 text

CoreAnimation ! • Allows to animate everything. Yes even that! • Out of process rendering - super fast. You can enroll custom callback into same pipeline5 • Chaining, delays, relative time… oh my! • Scrubbing using speed = 0 & timeOffset • It is not going away - Apple's behind it 5 https://www.objc.io/issues/12-animations/animating-custom-layer-properties/

Slide 43

Slide 43 text

CoreAnimation ! • With great power comes great complexity

Slide 44

Slide 44 text

No content

Slide 45

Slide 45 text

CoreAnimation ! • With great power comes great complexity • Stringly-typed, verbose API let anim = CABasicAnimation(keyPath: "transform.scale.xy") anim.fromValue = 0 anim.toValue = 1 anim.duration = 0.33 anim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn) anim.delegate = self anim.autoreverses = true anim.repeatCount = 3 anim.fillMode = kCAFillModeBackwards layer.add(anim, forKey: "my-key")

Slide 46

Slide 46 text

CoreAnimation ! • With great power comes great complexity • Stringly-typed, verbose API • Silently swallows type mismatches let anim = CABasicAnimation(keyPath: "opacity") anim.fromValue = CGAffineTransform(rotationAngle: .pi) anim.toValue = UIColor.whatever // ᕕ( ᐛ )ᕗ layer.add(anim, forKey: "silence-of-the-lambs")

Slide 47

Slide 47 text

CoreAnimation ! • With great power comes great complexity • Stringly-typed, verbose API • Silently swallows type mismatches • Interaction requires to infer presentation() func point(inside point: CGPoint, with event: UIEvent?) -> Bool { guard let presentation = animatedLayer.presentation() else { return false } let pointInLayer = convert(point, from: self, to: presentation) return presentation.frame.contains(pointInLayer) }

Slide 48

Slide 48 text

CoreAnimation ! • With great power comes great complexity • Stringly-typed, verbose API • Silently swallows type mismatches • Interaction requires to infer presentation() • Animation jumps: .isRemovedOnCompletion = false

Slide 49

Slide 49 text

CoreAnimation ! • With great power comes great complexity • Stringly-typed, verbose API • Silently swallows type mismatches • Interaction requires to infer presentation() • Animation jumps .isRemovedOnCompletion = false layer.model().position = layer.presentation()!.position layer.removeAnimation(forKey: "bounce-up")

Slide 50

Slide 50 text

CoreAnimation ! • For all of you control freaks out there • Build-your-own-API edition - if you are writing your framework, chances you will be using CoreAnimation (or CADisplayLink) • Doesn't pretend to be your best friend, but gets job done

Slide 51

Slide 51 text

CoreAnimation complexity ! ---×-> " control ! ---×-> " flexibility ! ---×-> "

Slide 52

Slide 52 text

UIDynamics

Slide 53

Slide 53 text

UIDynamics ! • Works with UIView subclasses let attachment = UIAttachmentBehavior( item: myView, offsetFromCenter: .zero attachedToAnchor: pan.location(in: view) ) animator.addBehavior(attachment)

Slide 54

Slide 54 text

UIDynamics ! • Works with UIView subclasses • Allows for a fairly broad range of physical simulations6 6 https://github.com/notjosh/Dynamically

Slide 55

Slide 55 text

UIDynamics ! • Works with UIView subclasses • Allows for a fairly broad range of physical simulations6 • Great for breathing a bit of life in your UI 6 https://github.com/notjosh/Dynamically

Slide 56

Slide 56 text

UIDynamics ! • Works with UIView subclasses • Allows for a fairly broad range of physical simulations6 • Great for breathing a bit of life in your UI • Fair amount of control: toggle individual behaviours, live update parameters 6 https://github.com/notjosh/Dynamically

Slide 57

Slide 57 text

UIDynamics ! • Works with UIView subclasses • Allows for a fairly broad range of physical simulations6 • Great for breathing a bit of life in your UI • Fair amount of control: toggle individual behaviours, live update parameters • Notorious for UICollectionView layout 6 https://github.com/notjosh/Dynamically

Slide 58

Slide 58 text

UIDynamics ! • Somewhat limited in what it can do

Slide 59

Slide 59 text

UIDynamics ! • Somewhat limited in what it can do Very focused

Slide 60

Slide 60 text

UIDynamics ! • Somewhat limited in what it can do Very focused • It is physics I:

Slide 61

Slide 61 text

UIDynamics !

Slide 62

Slide 62 text

UIDynamics ! • Somewhat limited in what it can do Very focused • It is physics I: slow to settle? • It is physics II:

Slide 63

Slide 63 text

UIDynamics !

Slide 64

Slide 64 text

UIDynamics !

Slide 65

Slide 65 text

UIDynamics ! • Somewhat limited in what it can do Very focused • It is physics I: slow to settle • It is physics II: hard to control

Slide 66

Slide 66 text

UIDynamics ! • Great for small delightful interactions for your UI • Even small games in UIDynamics7 • Very specialized • Please make sure it's appropriate - remember Apple Music artists selection UI. 7 http://fancypixel.github.io/blog/2015/06/19/playing-with-uidynamics-in-ios-9/

Slide 67

Slide 67 text

UIDynamics complexity ! -×---> " control ! -×---> " flexibility ! -×---> "

Slide 68

Slide 68 text

POP

Slide 69

Slide 69 text

POP ! • Nice API, reasonable defaults, easy to start with • Runs on main thread - makes interactive interruptible animations a breeze • Designed for realistic animations: throwing a view takes in account its velocity, putting a finger down makes the view stop etc

Slide 70

Slide 70 text

POP ! • Runs on main thread - will drop frames if you are abusing main thread • If you are building a framework vending animation, don't use POP if you can not guarantee consumers are performant enough

Slide 71

Slide 71 text

POP ! • Solves problem similar with UIDynamics, but extensible • Make sure your use-case is main thread-affined

Slide 72

Slide 72 text

POP complexity ! -×---> " control ! ---×-> " flexibility ! ---×-> "

Slide 73

Slide 73 text

SpriteKit ! • Apple's answer to Cocos2D - a game engine really • Just like GameplayKit can be used to spice up your apps8, SpriteKit can be repurposed, too • Powered by box2d while provides simpler interface • It's to UIDynamics what CoreAnimation to UIView animations • If you haven't heard yet, AR ❤ SpriteKit 8 https://realm.io/news/sash-zats-gameplaykit-beyond-games/

Slide 74

Slide 74 text

SpriteKit ! • Can't interface with UIKit as much • Physics is still very hard to control

Slide 75

Slide 75 text

SpriteKit ! • Game engine at your disposal. Sky is the limit • If you have some time on your hands and want to make your app more delightful (check out PCalc β about screen) • Physics ¯\_(ϑ)_/¯ • When designer comes up to you with "a physical simulation", make sure it is "actual physics"

Slide 76

Slide 76 text

SpriteKit complexity ! -×---> " control ! ---×-> " flexibility ! ---×-> "

Slide 77

Slide 77 text

Keyframes & Lottie† † http://airbnb.io/lottie/community-showcase.html

Slide 78

Slide 78 text

Keyframes & Lottie ! • All batteries included - load animation, start playing it • Built on top of CoreAnimation - super performant • Crossplatform

Slide 79

Slide 79 text

Keyframes & Lottie ! • Requires familiarity with After Effect • Keyframes can only scrub or replay

Slide 80

Slide 80 text

Keyframes & Lottie ! • Great for visually reach stories (someone has to make them) • Lottie provides more flexibility - you can tweak parameters of the AfterEffects file at runtime

Slide 81

Slide 81 text

Keyframes & Lottie complexity ! ×----> " control ! ××---> " flexibility ! ××---> "

Slide 82

Slide 82 text

Animations cookbook Use-case Technology Small quick animation, or you just feeling lazy UIView.animate(with… Interactive but not overly complicated or reactive UIViewPropertyAnimator Advanced animations, building a framework CoreAnimation

Slide 83

Slide 83 text

Animations cookbook Use-case Technology Simple physics, snapping, make UI feel alive POP or UIDynamics Elaborate interactive animation or complicated physics SceneKit You know AfterEffects, or can hire someone who does Keyframes or Lottie

Slide 84

Slide 84 text

Conclusions • Be aware of technologies, their ! and " - no bad ones* • Mind your project constraints • Write framework if it's missing: iOS haven't always supported AfterEffects animations * okay, fine, there are some

Slide 85

Slide 85 text

References • Advanced Animations with UIKit, WWDC 2017 • Advances in UIKit Animations and Transitions, WWDC 2016 • Advanced Graphics and Animations for iOS Apps, WWDC 2014 • Building Interruptible and Responsive Interactions, WWDC 2014 • Core Animation Essentials, WWDC 2011

Slide 86

Slide 86 text

References • UIDynamics guide http://samwize.com/ 2016/12/19/uikit-dynamics-guide/ • UICollectionView + UIKit Dynamics https:// www.objc.io/issues/5-ios7/collection-views-and- uidynamics/ • Animating Custom Layer Properties https:// www.objc.io/issues/12-animations/animating- custom-layer-properties

Slide 87

Slide 87 text

Feedback please ! @zats [email protected]