O slideshow foi denunciado.
Seu SlideShare está sendo baixado. ×

Александр Зимин – Анимация как средство самовыражения

Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Próximos SlideShares
Intro to Game Programming
Intro to Game Programming
Carregando em…3
×

Confira estes a seguir

1 de 95 Anúncio

Александр Зимин – Анимация как средство самовыражения

Baixar para ler offline

Расскажу о том, как создавать сложные анимации в iOS приложениях.
- CoreAnimation и его особенности
- Анимационные переходы между экранами
- Работа с анимациями, экспортированными из Adobe After Effects

Расскажу о том, как создавать сложные анимации в iOS приложениях.
- CoreAnimation и его особенности
- Анимационные переходы между экранами
- Работа с анимациями, экспортированными из Adobe After Effects

Anúncio
Anúncio

Mais Conteúdo rRelacionado

Diapositivos para si (20)

Semelhante a Александр Зимин – Анимация как средство самовыражения (20)

Anúncio

Mais de CocoaHeads (16)

Mais recentes (20)

Anúncio

Александр Зимин – Анимация как средство самовыражения

  1. 1. 1
  2. 2. 2 Animations ON iOS
  3. 3. 3 • Animations • CoreAnimation: control, shape layer • UIViewPropertyAnimator • Advanced Animations • Transitions
  4. 4. 4
  5. 5. 5 • Wrong password • Launch • Open Settings App • Bluetooth • Switch Off/On • Back
  6. 6. 6 0 100 view.frame.origin.x = 100
  7. 7. 7 0 100 view.frame.origin.x = 100
  8. 8. 8 0 100 view.frame.origin.x = 100 || time = 1.5
  9. 9. 9 0 100 view.frame.origin.x = 100 || time = 2.5 view.frame.origin.x = 100 || time = 2.5 || curve = .easeIn
  10. 10. 10 Timer.scheduledTimer(withTimeInterval: 1 / 60, repeats: true) { (_) in self.applyAnimationStep() }
  11. 11. 11 Timer.scheduledTimer(withTimeInterval: 1 / 60, repeats: true) { (_) in self.applyAnimationStep() } displayLink = CADisplayLink(target: self, selector: #selector(applyAnimationStep)) displayLink.add(to: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
  12. 12. 12 Advanced Graphics and Animations for iOS Apps
  13. 13. 13
  14. 14. 14 CoreAnimation Timer-Based • Optimization • UIKit based • Custom property animation • State observing (< iOS10) • Custom timing function (hi, speed 👋 )
  15. 15. 15 CoreAnimation Timer-Based • CoreAnimation • UIView animations • UIViewPropertyAnimator (iOS 10+) • CADisplayLink • POP • All other external frameworks
  16. 16. 16 CoreAnimation
  17. 17. 17 Alexander Zimin – Анимация в iOS
  18. 18. 18 • Work with CALayer’s • Shared for iOS/macOS • Unfamiliar syntax • Powerfull
  19. 19. 19 let animation = CABasicAnimation(keyPath: "position.x") animation.duration = 1 animation.fromValue = animationView.layer.position.x animation.toValue = 50 animationView.layer.add(animation, forKey: "name") animationView.layer.position.x = 50
  20. 20. 20 let animation = CABasicAnimation(keyPath: "position.x") animation.duration = 1 animation.fromValue = animationView.layer.position.x animation.toValue = 50 animationView.layer.add(animation, forKey: "name") animationView.layer.position.x = 50
  21. 21. 21 Presentation Layer Modal Layer
  22. 22. 22 0 100
  23. 23. 23 0 100
  24. 24. 24 0 100 view.frame.origin.x = 100
  25. 25. 25 • CABasicAnimation • CAKeyframeAnimation • CAAnimationGroup • CASpringAnimation (iOS 9+)
  26. 26. 26 • bounds • contents • masksToBounds • shadowColor Animatable Properties (not all)
  27. 27. 27 Control animation
  28. 28. 28
  29. 29. 29 animation = CABasicAnimation(keyPath: "position.y") animation.duration = 2 animation.fromValue = label.layer.position.y animation.toValue = 30 label.layer.add(animation, forKey: "animation2") label.layer.speed = 0 label.layer.position.y = 30
  30. 30. 30 label.layer.timeOffset = value * animation.duration value from 0 to 1
  31. 31. 31 CAShapeLayer
  32. 32. 32
  33. 33. 33 shapeLayer = CAShapeLayer() animation = CABasicAnimation(keyPath: "path") animation.fromValue = firstPath.cgPath animation.toValue = secondPath.cgPath
  34. 34. 34 shapeLayer = CAShapeLayer() animation = CABasicAnimation(keyPath: "strokeEnd") animation.fromValue = 0 animation.toValue = 1
  35. 35. 35
  36. 36. 36 let firstPath = UIBezierPath( roundedRect: CGRect(x: 100, y: 100, width: 100, height: 100), byRoundingCorners: [.bottomRight, .topRight], cornerRadii: CGSize(width: 30, height: 30) ) let secondPath = UIBezierPath( rect: CGRect(x: 100, y: 100, width: 100, height: 100) )
  37. 37. 37 let firstPath = UIBezierPath( roundedRect: CGRect(x: 100, y: 100, width: 100, height: 100), byRoundingCorners: [.bottomRight, .topRight], cornerRadii: CGSize(width: 30, height: 30) ) let secondPath = UIBezierPath( rect: CGRect(x: 100, y: 100, width: 100, height: 100) )
  38. 38. 38
  39. 39. 39
  40. 40. 40 let firstPath = UIBezierPath() firstPath.move(to: CGPoint(x: 0, y: 0)) firstPath.addLine(to: CGPoint(x: 60, y: 0)) firstPath.addQuadCurve(to: CGPoint(x: 100, y: 40), controlPoint: CGPoint(x: 100, y: 0)) firstPath.addLine(to: CGPoint(x: 100, y: 60)) firstPath.addQuadCurve(to: CGPoint(x: 60, y: 100), controlPoint: CGPoint(x: 100, y: 100)) firstPath.addLine(to: CGPoint(x: 0, y: 100)) firstPath.addLine(to: CGPoint(x: 0, y: 0))
  41. 41. let firstPath = UIBezierPath() firstPath.move(to: CGPoint(x: 0, y: 0)) firstPath.addLine(to: CGPoint(x: 60, y: 0)) firstPath.addQuadCurve(to: CGPoint(x: 100, y: 40), controlPoint: CGPoint(x: 100, y: 0)) firstPath.addLine(to: CGPoint(x: 100, y: 60)) firstPath.addQuadCurve(to: CGPoint(x: 60, y: 100), controlPoint: CGPoint(x: 100, y: 100)) firstPath.addLine(to: CGPoint(x: 0, y: 100)) firstPath.addLine(to: CGPoint(x: 0, y: 0)) 41
  42. 42. 42
  43. 43. 43 let secondPath = UIBezierPath() secondPath.move(to: CGPoint(x: 0, y: 0)) secondPath.addLine(to: CGPoint(x: 100, y: 0)) secondPath.addLine(to: CGPoint(x: 100, y: 0)) secondPath.addLine(to: CGPoint(x: 100, y: 100)) secondPath.addLine(to: CGPoint(x: 100, y: 100)) secondPath.addLine(to: CGPoint(x: 0, y: 100)) secondPath.addLine(to: CGPoint(x: 0, y: 0))
  44. 44. 44 let secondPath = UIBezierPath() secondPath.move(to: CGPoint(x: 0, y: 0)) secondPath.addLine(to: CGPoint(x: 100, y: 0)) secondPath.addLine(to: CGPoint(x: 100, y: 0)) secondPath.addLine(to: CGPoint(x: 100, y: 100)) secondPath.addLine(to: CGPoint(x: 100, y: 100)) secondPath.addLine(to: CGPoint(x: 0, y: 100)) secondPath.addLine(to: CGPoint(x: 0, y: 0))
  45. 45. 45
  46. 46. 46
  47. 47. 47 No such property
  48. 48. 48
  49. 49. 49
  50. 50. 50 let differenceInSize = label.font.pointSize / anotherLabel.font.pointSize self.anotherLabel.transform = CGAffineTransform(scaleX: differenceInSize, y: differenceInSize) self.anotherLabel.center = self.label.center self.label.alpha = 1 self.anotherLabel.alpha = 0
  51. 51. 51 let differenceInSize = label.font.pointSize / anotherLabel.font.pointSize self.anotherLabel.transform = CGAffineTransform(scaleX: differenceInSize, y: differenceInSize) self.anotherLabel.center = self.label.center self.label.alpha = 1 self.anotherLabel.alpha = 0 a a
  52. 52. 52 let differenceInSize = label.font.pointSize / anotherLabel.font.pointSize self.anotherLabel.transform = CGAffineTransform(scaleX: differenceInSize, y: differenceInSize) self.anotherLabel.center = self.label.center self.label.alpha = 1 self.anotherLabel.alpha = 0
  53. 53. 53 propertyAnimation = UIViewPropertyAnimator(duration: 0.5, curve: .easeIn) { self.label.alpha = 0 self.anotherLabel.alpha = 1 self.label.center.y = 50 self.anotherLabel.center.y = 50 self.label.transform = CGAffineTransform(scaleX: 1 / differenceInSize, y: 1 / differenceInSize) self.anotherLabel.transform = CGAffineTransform.identity }
  54. 54. 54 UIViewPropertyAnimator
  55. 55. 55 propertyAnimation = UIViewPropertyAnimator( duration: 1, curve: .easeIn, animations: { self.view.alpha = 0 } ) propertyAnimation.startAnimation()
  56. 56. 56 • var isReversed: Bool { get set } • var fractionComplete: CGFloat { get set } • func continueAnimation(
 withTimingParameters parameters: UITimingCurveProvider?, 
 durationFactor: CGFloat)
  57. 57. 57 🕵
  58. 58. 58 class ObservableLayer: CALayer { override func add(_ anim: CAAnimation, forKey key: String?) { print(anim, key) super.add(anim, forKey: key) } }
  59. 59. 59 animation = UIViewPropertyAnimator(duration: 2, curve: .linear, animations: nil) animation.addAnimations { self.animationView.alpha = 0.2 } animation.startAnimation() animation.fractionComplete = 0.2 On Start On Touch
  60. 60. 60 On Start
  61. 61. 61 <CABasicAnimation:0x610000024220; toValue = 0.20000000298023224; removedOnCompletion = 0; delegate = <UIViewAnimationState: 0x7fe69e0094e0>; fillMode = both; timingFunction = linear; duration = 2; fromValue = 1; keyPath = opacity> 1 opacity
  62. 62. 62 opacity = 1 -> 0.2 duration = 2 timingFunction = linear <CABasicAnimation:0x610000024220; toValue = 0.20000000298023224; removedOnCompletion = 0; delegate = <UIViewAnimationState: 0x7fe69e0094e0>; fillMode = both; timingFunction = linear; duration = 2; fromValue = 1; keyPath = opacity> 1 opacity
  63. 63. 63 <CABasicAnimation:0x610000027340; removedOnCompletion = 0; delegate = <UIViewAnimationState: 0x7fe69e0094e0>; fillMode = both; timingFunction = linear; duration = 2; toValue = 100; fromValue = 0.0; keyPath = uiFractionalProgress> 2 UIPacingAnimationForAnimatorsKey
  64. 64. 64 <CABasicAnimation:0x610000027340; removedOnCompletion = 0; delegate = <UIViewAnimationState: 0x7fe69e0094e0>; fillMode = both; timingFunction = linear; duration = 2; toValue = 100; fromValue = 0.0; keyPath = uiFractionalProgress> 2 UIPacingAnimationForAnimatorsKey uiFractionalProgress = 0 -> 100 duration = 2 timingFunction = linear (lldb) po self.presentation()?.value(forKey: "uiFractionalProgress") ▿ Optional<Any>
  65. 65. 65 On Touch
  66. 66. 66 UIPacingAnimationForAnimatorsKey 2 x3 opacity 1 x3
  67. 67. 67 UIPacingAnimationForAnimatorsKey 2 x3 opacity 1 x3 • speed = 0 
 on 4 first • beginTime = -0.398438 on 2 last
  68. 68. 68 • By changing fractionComplete you spamming new CABasicAnimation objects
  69. 69. 69 Advanced Animations
  70. 70. 70 • Receipt: • Good Designer 👑 • AfterEffects • Lottie + bodymovin • Done 🎉
  71. 71. 71
  72. 72. 72
  73. 73. 73
  74. 74. 74
  75. 75. 75 bodymovin
  76. 76. 76 bodymovin
  77. 77. 77
  78. 78. 78 animationView = LOTAnimationView(name: "animation") self.view.addSubview(animationView) animationView.play() animationView.animationProgress = CGFloat(sender.value)
  79. 79. 79
  80. 80. 80 animationView = LOTAnimationView(json: json)
  81. 81. 81 let filepath = Bundle.main.path(forResource: "animation", ofType: "json")! var contents = try! String(contentsOfFile: filepath) contents = contents.replacingOccurrences( of: "[0.6790901,0.178386,0.178386,1]", with: “[0,0.9,0,1]" ) contents = contents.replacingOccurrences( of: "[0.6784314,0.1764706,0.1764706,1]", with: “[0,0.9,0,1]" ) let data = contents.data(using: .utf8)! let json = try! JSONSerialization.jsonObject( with: data, options: [] ) as? [String: Any] animationView = LOTAnimationView(json: json)
  82. 82. 82 let filepath = Bundle.main.path(forResource: "animation", ofType: "json")! var contents = try! String(contentsOfFile: filepath) contents = contents.replacingOccurrences( of: "[0.6790901,0.178386,0.178386,1]", with: “[0,0.9,0,1]" ) contents = contents.replacingOccurrences( of: "[0.6784314,0.1764706,0.1764706,1]", with: “[0,0.9,0,1]" ) let data = contents.data(using: .utf8)! let json = try! JSONSerialization.jsonObject( with: data, options: [] ) as? [String: Any] animationView = LOTAnimationView(json: json)
  83. 83. 83 let filepath = Bundle.main.path(forResource: "animation", ofType: "json")! var contents = try! String(contentsOfFile: filepath) contents = contents.replacingOccurrences( of: "[0.6790901,0.178386,0.178386,1]", with: “[0,0.9,0,1]" ) contents = contents.replacingOccurrences( of: "[0.6784314,0.1764706,0.1764706,1]", with: “[0,0.9,0,1]" ) let data = contents.data(using: .utf8)! let json = try! JSONSerialization.jsonObject( with: data, options: [] ) as? [String: Any] animationView = LOTAnimationView(json: json) Mutating Phase
  84. 84. 84 let filepath = Bundle.main.path(forResource: "animation", ofType: "json")! var contents = try! String(contentsOfFile: filepath) contents = contents.replacingOccurrences( of: "[0.6790901,0.178386,0.178386,1]", with: “[0,0.9,0,1]" ) contents = contents.replacingOccurrences( of: "[0.6784314,0.1764706,0.1764706,1]", with: “[0,0.9,0,1]" ) let data = contents.data(using: .utf8)! let json = try! JSONSerialization.jsonObject( with: data, options: [] ) as? [String: Any] animationView = LOTAnimationView(json: json)
  85. 85. 85
  86. 86. 86
  87. 87. 87 • JSON • Size (in both ways) • Mutatablity • Control • Speed • Progress • Completion • All AfterEffects magic
  88. 88. 88 So
  89. 89. 89 • Use CAShapeLayers • Combine multiply layers: • Use alpha/scale/mask/… • Use time-based animations
  90. 90. 90 Transitions
  91. 91. 91 Alexander Zimin – UIViewController, откройся!
  92. 92. 92 UIPercentDrivenInteractiveTransition
  93. 93. 93 • Avoid time-based animations 🚀 • Implement user interactions (it’s easy 😉) • Create delegate for your custom animations on VCs 🔊 • Check github.com/azimin/AZTransitions 👍
  94. 94. 94 • Offscreen rendering
  95. 95. 95 @ZiminAlex

×