Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Давайте использовать анимации грамотно

Давайте использовать анимации грамотно

Talk by Yevhenii Peteliev.

Речь пойдет о:

1. Роли анимаций в приложениях.
2. Инструментах и подходах к построениям анимаций.
3. Примерах использования анимаций и их реализациях.

This talk was made for CocoaFriday #3 ( https://cocoaheads.org.ua/cocoafriday/3) which took place May 10, 2019.

Video: https://youtu.be/6R55YbJimlc

Db84cf61fdada06b63f43f310b68b462?s=128

CocoaHeads Ukraine

May 10, 2019
Tweet

Transcript

  1. Proper use of animations !" 1 Yevhenii Peteliev Mac Software

    Engineer, MacPaw Inc.
  2. Agenda: Why do you need to use animations? $ How

    does animation work? Learn new about animation types ! What should you know about Core Animation? & Understanding fluid interfaces ' Introduction to UIViewPropertyAnimator ( How to create advanced animations? ) Tips&Tricks 2
  3. Why do we need to use animations? *+ 3

  4. 4 World without animations

  5. 5 And what about animations?

  6. So, animation… ' Reduces cognitive load . Helps to notice

    changes / Creates interrelation between components 0 Brings the user interface to life 6
  7. Is it animation? 7

  8. Yep, it is animation 8

  9. Soo, boring… 9

  10. Timing Functions 10

  11. Timer " 11 Timer.scheduledTimer(withTimeInterval: 1 / 60, repeats: true) {

    (_) in self.applyAnimationStep() }
  12. 12 displayLink = CADisplayLink(target: self, selector:#selector(applyAnimationStep)) displayLink.add(to: .current, forMode: .default)

    DisplayLink 7 Timer.scheduledTimer(withTimeInterval: 1 / 60, repeats: true) { (_) in self.applyAnimationStep() } ✅ ❌
  13. Why is CADisplayLink better? 13

  14. CADisplayLink 14 < Always fires immediately prior to the screen

    being redrawn = Has an integer frameInterval property that specifies how many frames to skip each time it fires > If CADisplayLink misses a scheduled frame, it will skip it altogether and update at the next scheduled frame time
  15. How does animation go? 15

  16. Animation process 16 Advanced Graphics and Animations for iOS Apps

  17. Animation Types 17

  18. Сhoice 18 Core Animation Time-Based

  19. Core Animation Optimization UIKit based Custom transitions 19 Benefits:

  20. ☺ Custom property State observing (iOS<10) Custom timing function 20

    Time-Based Benefits:
  21. Core Animation UIView animation UIViewPropertyAnimator 21 Representatives: Core Animation

  22. Timer/CADisplayLink Facebook POP Lottie Others 22 Representatives: Time-Based

  23. Why CoreAnimation? QR 23

  24. Core Animation S 24 T Fast = Shared both for

    iOS/macOS U Use CALayer’s: V CAShapeLayer W CAGradientLayer > CAReplicatorLayer X CAEmitterLayer Y Others
  25. CALayer 25 bounds contents borderColor backgroundColor transform opacity others

  26. CABasicAnimation " 26 Provides basic, single-keyframe animation capabilities for a

    layer property
  27. 27 let positionAnimation = CABasicAnimation(keyPath: “position.x") positionAnimation.fromValue = coloredLayer.position.x positionAnimation.toValue

    = 400 positionAnimation.duration = 1.0 coloredLayer.add(positionAnimation, forKey: nil) CABasicAnimation "
  28. 28 CABasicAnimation "

  29. 29 let positionAnimation = CABasicAnimation(keyPath: “position.x") positionAnimation.fromValue = coloredLayer.position.x positionAnimation.toValue

    = 400 positionAnimation.duration = 1.0 positionAnimation.isRemovedOnCompletion = false coloredLayer.add(positionAnimation, forKey: nil) CABasicAnimation "
  30. 30 Presentation Model

  31. 31 let newPositionX: CGFloat = 400 let positionAnimation = CABasicAnimation(keyPath:

    "position.x") positionAnimation.fromValue = coloredLayer.position.x positionAnimation.toValue = newPositionX positionAnimation.duration = 1 coloredLayer.add(positionAnimation, forKey: nil) coloredLayer.position.x = newPositionX CABasicAnimation "
  32. 32 CABasicAnimation "

  33. 33 let newPositionX: CGFloat = 400 let positionAnimation = CABasicAnimation(keyPath:

    "position.x") positionAnimation.fromValue = coloredLayer.position.x positionAnimation.toValue = newPositionX positionAnimation.duration = 1 coloredLayer.add(positionAnimation, forKey: nil) CATransaction.begin() CATransaction.setDisableActions(true) coloredLayer.position.x = newPositionX CATransaction.commit() CABasicAnimation "
  34. 34 CABasicAnimation "

  35. CAKeyframeAnimation ^ 35 Provides keyframe animation capabilities for a layer

    object
  36. 36 CAKeyframeAnimation ^

  37. CAKeyframeAnimation ^ 37 let newPositionX: CGFloat = 400 let positionAnimation

    = CAKeyframeAnimation(keyPath: "position.x") positionAnimation.values = [coloredLayer.position.x, 400, 200, newPositionX] positionAnimation.duration = 1.0 coloredLayer.add(positionAnimation, forKey: nil) CATransaction.begin() CATransaction.setDisableActions(true) coloredLayer.position.x = newPositionX CATransaction.commit()
  38. CASpringAnimation ` 38 Applies a spring-like force to a layer's

    properties Allows control over physically based attributes such as the spring's damping and stiffness
  39. 39 CASpringAnimation `

  40. 40 let newPositionX: CGFloat = 400 let positionAnimation = CASpringAnimation(keyPath:

    "position.x") positionAnimation.fromValue = coloredLayer.position.x positionAnimation.toValue = newPositionX positionAnimation.duration = positionAnimation.settlingDuration coloredLayer.add(positionAnimation, forKey: nil) CATransaction.begin() CATransaction.setDisableActions(true) coloredLayer.position.x = newPositionX CATransaction.commit() CASpringAnimation `
  41. What about some practice? 41

  42. 42 Simple Animation

  43. 43 let newScale: CGFloat = 2 let newBorderWidth: CGFloat =

    10 let newBorderColor = NSColor.green.cgColor let newBackgroundColor = NSColor.red.cgColor let borderWidthAnimation = CABasicAnimation(keyPath: "borderWidth") borderWidthAnimation.fromValue = coloredLayer.borderWidth borderWidthAnimation.toValue = newBorderWidth let borderColorAnimation = CABasicAnimation(keyPath: "borderColor") borderColorAnimation.fromValue = coloredLayer.borderColor borderColorAnimation.toValue = newBorderColor let backgroundColorAnimation = CABasicAnimation(keyPath: "backgroundColor") backgroundColorAnimation.fromValue = coloredLayer.backgroundColor backgroundColorAnimation.toValue = newBackgroundColor let scaleAnimation = CABasicAnimation(keyPath: "transform.scale") scaleAnimation.fromValue = 1 scaleAnimation.toValue = newScale Simple Animation
  44. 44 … let groupAnimation = CAAnimationGroup() groupAnimation.duration = 2.0 groupAnimation.timingFunction

    = CAMediaTimingFunction(name: .easeInEaseOut) groupAnimation.animations = [borderWidthAnimation, borderColorAnimation, backgroundColorAnimation, scaleAnimation] coloredLayer.add(groupAnimation, forKey: nil) CATransaction.begin() CATransaction.setDisableActions(true) coloredLayer.borderWidth = newBorderWidth coloredLayer.borderColor = newBorderColor coloredLayer.backgroundColor = newBackgroundColor coloredLayer.transform = CATransform3DMakeScale(newScale, newScale, 1) CATransaction.commit() Simple Animation
  45. Fluid Interfaces 45

  46. Fluid Interfaces 46

  47. 47 Improve the user’s experience, making every interaction feel quick,

    lightweight and meaningful Give the user a feeling of control, which builds trust with your app Fluid Interfaces
  48. 48 Calculator Button Buttons that feel responsive, acknowledging to the

    user that they are functional We want the action to be cancellable if the user decides against their action
  49. 49 Spring Animations Springs make great animation models because of

    their speed and natural appearance This is perfect for creating interfaces that feel responsive— they spring to life!
  50. 50 Flashlight Button Create a button that was easily and

    quickly accessible, but couldn’t be triggered accidentally The button is springy and grows as the user applies force, hinting at the required gesture
  51. 51 Rubberbanding Rubberbanding is a great way to communicate invalid

    actions while still giving the user a sense of control It softly indicates a boundary, pulling them back into a valid state
  52. 52 Acceleration Pausing Interface reaction time is based on the

    user’s motion If they quickly pause, the interface quickly responds If they slowly pause, it slowly responds
  53. 53 Rewarding Momentum ☺ A drawer with open and closed

    states that has bounciness based on the velocity of the gesture When the user swipes a view with velocity, it’s much more satisfying to animate the view with bounciness When the drawer is tapped, it animates without bounciness
  54. 54 FaceTime PiP Continuous animation that respects the gesture’s initial

    velocity
  55. 55 Rotation Applying the concepts from the PiP interface to

    a rotation animation
  56. Sooo 56 Apply these tips to your components Teach designers

    about new possibilities Use these approach into your projects You’re a rockstar!
  57. UIViewPropertyAnimator 57

  58. UIViewPropertyAnimator 58 Animates changes to views and allows the dynamic

    modification of those animations Operates on animatable properties of views (frame, alpha, transform, etc), creating animations from the blocks you provide
  59. 59 Scrub through a paused animation by modifying the fractionComplete

    property Change the animation’s direction using the isReversed property Start, pause, resume and stop animations UIViewPropertyAnimator
  60. What about some practice? 60

  61. 61 Animatable View

  62. 62 let viewSize = CGSize(width: 70, height: 70) let viewOrigin

    = CGPoint(x: 0, y: view.frame.height - 2 * viewSize.height) let coloredView = UIView(frame: CGRect(origin: viewOrigin, size: viewSize)) coloredView.backgroundColor = .red view.addSubview(coloredView) Animatable View
  63. 63 let coloredViewAnimator = UIViewPropertyAnimator(duration: 2.5, curve: .easeInOut) coloredViewAnimator.addAnimations {

    coloredView.backgroundColor = .green coloredView.transform = CGAffineTransform(scaleX: 2, y: 2) coloredView.center = CGPoint(x: self.view.frame.midX, y: 2 * viewSize.height) } Animatable View
  64. 64 Start / Pause

  65. 65 // Starts the animation from an inactive state or

    if it has been paused coloredViewAnimator.startAnimation() 
 // Pauses an active, running animation, or starts the animation if paused coloredViewAnimator.pauseAnimation() Start / Pause
  66. 66 Stop

  67. 67 // Stops the animation coloredViewAnimator.stopAnimation(true) Stop

  68. 68 Progress

  69. 69 // The completion percentage of the animation coloredViewAnimator.fractionComplete =

    CGFloat(sender.value) Progress
  70. 70 Reverse

  71. 71 // Reversed indicates that the animation is running in

    the reversed direction coloredViewAnimator.isReversed = sender.isOn Reverse
  72. Advanced Animations †⭐ 72

  73. 73 ˆ Lottie is a library for Android, iOS, Web

    and Windows that parses Adobe After Effects animations exported as JSON with Bodymovin and renders them natively on mobile and on the web! Lottie ‰
  74. Lottie ‰ 74

  75. Lottie ‰ 75

  76. What do we need? Š‹ 76

  77. Requirements Œ 77 • Creative designer ` Adobe After Effects

    ) Bodymovin Ž Lottie library That’s all
  78. 78 Animatable Checkmark ✅

  79. 79 Animatable Checkmark ✅

  80. 80 let animation = Animation.named(“checkmark_1”) let checkmarkView = AnimationView(animation: animation)

    view.addSubview(checkmarkView) Animatable Checkmark ✅
  81. 81 Animatable Checkmark ✅

  82. 82 // play button clicked checkmarkView.play() // slider changed value

    checkmarkView.currentProgress = AnimationProgressTime(slider.floatValue) Animatable Checkmark ✅
  83. 83 Animatable Checkmark ☑

  84. 84 let fillKeypath = AnimationKeypath(keypath: "**.Fill 1.Color") let redValueProvider =

    ColorValueProvider(Color(r: 1, g: 0, b: 0, a: 0.5)) checkmarkView.setValueProvider(redValueProvider, keypath: fillKeypath) let strokeKeypath = AnimationKeypath(keypath: "**.Stroke 1.Color") let blueValueProvider = ColorValueProvider(Color(r: 0, g: 0, b: 1, a: 0.5)) checkmarkView.setValueProvider(blueValueProvider, keypath: strokeKeypath) Animatable Checkmark ☑
  85. Where can I use it? ‘’ 85

  86. Onboarding ‰ 86

  87. Advantages 87 Size Mutability Control Speed Progress Completion

  88. Tips&Tricks 0’ 88

  89. Wallpaper Wizard 89

  90. Animatable Wallpapers •* 90

  91. final public class DesktopImageWindow: NSWindow { convenience init(with image: CGImage,

    screen: NSScreen) { // setup window contentView.layer = CAShapeLayer() contentView.wantsLayer = true setup(with: image) } func setup(with image: CGImage) { guard let contentLayer = contentView?.layer else { return } contentLayer.contents = image } } 91 Animatable Wallpapers •*
  92. Animatable Menu Bar Icon –R 92

  93. final public class MenuController { private var timer: Timer? func

    animate() { invalidateTimer() timer = Timer.scheduledTimer(timeInterval: 1.0 / 30.0, target: self, selector: #selector(timerTick), userInfo: nil, repeats: true) } } 93 Animatable Menu Bar Icon –R
  94. final public class MenuController { … private var frameIndex =

    0 private let statusItemButton: NSStatusBarButton private let menuIcons = [NSImage(named: "1"), NSImage(named: "2"), ... ] @objc private func invalidateTimer() { timer?.invalidate() timer = nil frameIndex = 0 statusItemButton.image = menuIcons[0] } @objc private func timerTick() { statusItemButton.image = menuIcons[frameIndex] frameIndex = (frameIndex + 1) % menuIcons.count } } 94 Animatable Menu Bar Icon –R
  95. CleanMyMac X `^ 95

  96. Rain Flow Animation 96

  97. 97 final public class RainFlowView: NSView { … private let

    numberOfLines = 10 private var dashLayers: [CAShapeLayer] = [] } Rain Flow Animation
  98. 98 final public class RainFlowView: NSView { … public class

    Rule { let speed: Float let color: NSColor let dashPattern: [Int] } private var currentRule: Rule = .intro public func set(rule: Rule) { let path = buildPath(at: index, with: rule) dashLayers .enumerated() .forEach { index, layer in layer.path = path layer.dashPattern = rule.dashPattern layer.strokeColor = rule.color.cgColor } } } Rain Flow Animation
  99. Conclusion ™š 99 < Use animations properly = Combine multiple

    layers > Use Core Animation where it is possible W Don’t be afraid to use time-based animations Y Start using new features and concepts
  100. Links: " https://github.com/facebook/pop ! https://github.com/airbnb/lottie-ios ›https://github.com/nathangitter/fluid-interfaces Ž https://developer.apple.com/design/human- interface-guidelines/ œ

    https://developer.apple.com/library/archive/ documentation/Cocoa/Conceptual/CoreAnimation_guide/ Introduction/Introduction.html • https://lottiefiles.com 100
  101. Thank you for your attention! 101

  102. Sooo…questions? Ÿ& 102

  103. Contacts: Email: zhenya.peteliev@macpaw.com ¡ Medium: peteliev ¢ Twitter: peteliev £

    Facebook: peteliev 103