1
2
Animations
ON iOS
3
• Animations
• CoreAnimation: control, shape layer
• UIViewPropertyAnimator
• Advanced Animations
• Transitions
4
5
• Wrong password
• Launch
• Open Settings App
• Bluetooth
• Switch Off/On
• Back
6
0 100
view.frame.origin.x = 100
7
0 100
view.frame.origin.x = 100
8
0 100
view.frame.origin.x = 100 || time = 1.5
9
0 100
view.frame.origin.x = 100 || time = 2.5
view.frame.origin.x = 100 || time = 2.5 || curve = .easeIn
10
Timer.scheduledTimer(withTimeInterval: 1 / 60, repeats: true)
{ (_) in
self.applyAnimationStep()
}
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
Advanced Graphics and Animations for iOS Apps
13
14
CoreAnimation Timer-Based
• Optimization
• UIKit based
• Custom property animation
• State observing (< iOS10)
• Custom timing function (hi,
speed 👋 )
15
CoreAnimation Timer-Based
• CoreAnimation
• UIView animations
• UIViewPropertyAnimator (iOS 10+)
• CADisplayLink
• POP
• All other external frameworks
16
CoreAnimation
17
Alexander Zimin – Анимация в iOS
18
• Work with CALayer’s
• Shared for iOS/macOS
• Unfamiliar syntax
• Powerfull
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
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
Presentation Layer Modal Layer
22
0 100
23
0 100
24
0 100
view.frame.origin.x = 100
25
• CABasicAnimation
• CAKeyframeAnimation
• CAAnimationGroup
• CASpringAnimation (iOS 9+)
26
• bounds
• contents
• masksToBounds
• shadowColor
Animatable Properties (not all)
27
Control animation
28
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
label.layer.timeOffset = value * animation.duration
value from 0 to 1
31
CAShapeLayer
32
33
shapeLayer = CAShapeLayer()
animation = CABasicAnimation(keyPath: "path")
animation.fromValue = firstPath.cgPath
animation.toValue = secondPath.cgPath
34
shapeLayer = CAShapeLayer()
animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 1
35
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
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
39
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))
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
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
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
46
47
No such property
48
49
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
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
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
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
UIViewPropertyAnimator
55
propertyAnimation = UIViewPropertyAnimator(
duration: 1,
curve: .easeIn,
animations: {
self.view.alpha = 0
}
)
propertyAnimation.startAnimation()
56
• var isReversed: Bool { get set }
• var fractionComplete: CGFloat { get set }
• func continueAnimation(

withTimingParameters parameters:
UITimingCurveProvider?, 

durationFactor: CGFloat)
57
🕵
58
class ObservableLayer: CALayer {
override func add(_ anim: CAAnimation, forKey key: String?) {
print(anim, key)
super.add(anim, forKey: key)
}
}
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
On Start
61
<CABasicAnimation:0x610000024220; toValue =
0.20000000298023224; removedOnCompletion = 0;
delegate = <UIViewAnimationState:
0x7fe69e0094e0>; fillMode = both; timingFunction
= linear; duration = 2; fromValue = 1; keyPath =
opacity>
1
opacity
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
<CABasicAnimation:0x610000027340;
removedOnCompletion = 0; delegate =
<UIViewAnimationState: 0x7fe69e0094e0>; fillMode
= both; timingFunction = linear; duration = 2;
toValue = 100; fromValue = 0.0; keyPath =
uiFractionalProgress>
2
UIPacingAnimationForAnimatorsKey
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
On Touch
66
UIPacingAnimationForAnimatorsKey
2 x3
opacity
1 x3
67
UIPacingAnimationForAnimatorsKey
2 x3
opacity
1 x3
• speed = 0 

on 4 first
• beginTime = -0.398438
on 2 last
68
• By changing fractionComplete you
spamming new CABasicAnimation objects
69
Advanced Animations
70
• Receipt:
• Good Designer 👑
• AfterEffects
• Lottie + bodymovin
• Done 🎉
71
72
73
74
75
bodymovin
76
bodymovin
77
78
animationView = LOTAnimationView(name: "animation")
self.view.addSubview(animationView)
animationView.play()
animationView.animationProgress = CGFloat(sender.value)
79
80
animationView = LOTAnimationView(json: json)
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
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
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
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
86
87
• JSON
• Size (in both ways)
• Mutatablity
• Control
• Speed
• Progress
• Completion
• All AfterEffects magic
88
So
89
• Use CAShapeLayers
• Combine multiply layers:
• Use alpha/scale/mask/…
• Use time-based animations
90
Transitions
91
Alexander Zimin – UIViewController, откройся!
92
UIPercentDrivenInteractiveTransition
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
• Offscreen rendering
95
@ZiminAlex

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

  • 1.
  • 2.
  • 3.
    3 • Animations • CoreAnimation:control, shape layer • UIViewPropertyAnimator • Advanced Animations • Transitions
  • 4.
  • 5.
    5 • Wrong password •Launch • Open Settings App • Bluetooth • Switch Off/On • Back
  • 6.
  • 7.
  • 8.
  • 9.
    9 0 100 view.frame.origin.x =100 || time = 2.5 view.frame.origin.x = 100 || time = 2.5 || curve = .easeIn
  • 10.
    10 Timer.scheduledTimer(withTimeInterval: 1 /60, repeats: true) { (_) in self.applyAnimationStep() }
  • 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 Advanced Graphics andAnimations for iOS Apps
  • 13.
  • 14.
    14 CoreAnimation Timer-Based • Optimization •UIKit based • Custom property animation • State observing (< iOS10) • Custom timing function (hi, speed 👋 )
  • 15.
    15 CoreAnimation Timer-Based • CoreAnimation •UIView animations • UIViewPropertyAnimator (iOS 10+) • CADisplayLink • POP • All other external frameworks
  • 16.
  • 17.
    17 Alexander Zimin –Анимация в iOS
  • 18.
    18 • Work withCALayer’s • Shared for iOS/macOS • Unfamiliar syntax • Powerfull
  • 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 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.
  • 22.
  • 23.
  • 24.
  • 25.
    25 • CABasicAnimation • CAKeyframeAnimation •CAAnimationGroup • CASpringAnimation (iOS 9+)
  • 26.
    26 • bounds • contents •masksToBounds • shadowColor Animatable Properties (not all)
  • 27.
  • 28.
  • 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 label.layer.timeOffset = value* animation.duration value from 0 to 1
  • 31.
  • 32.
  • 33.
    33 shapeLayer = CAShapeLayer() animation= CABasicAnimation(keyPath: "path") animation.fromValue = firstPath.cgPath animation.toValue = secondPath.cgPath
  • 34.
    34 shapeLayer = CAShapeLayer() animation= CABasicAnimation(keyPath: "strokeEnd") animation.fromValue = 0 animation.toValue = 1
  • 35.
  • 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 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.
  • 39.
  • 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.
    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.
  • 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 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.
  • 46.
  • 47.
  • 48.
  • 49.
  • 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 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 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 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.
  • 55.
    55 propertyAnimation = UIViewPropertyAnimator( duration:1, curve: .easeIn, animations: { self.view.alpha = 0 } ) propertyAnimation.startAnimation()
  • 56.
    56 • var isReversed:Bool { get set } • var fractionComplete: CGFloat { get set } • func continueAnimation(
 withTimingParameters parameters: UITimingCurveProvider?, 
 durationFactor: CGFloat)
  • 57.
  • 58.
    58 class ObservableLayer: CALayer{ override func add(_ anim: CAAnimation, forKey key: String?) { print(anim, key) super.add(anim, forKey: key) } }
  • 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.
  • 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 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 <CABasicAnimation:0x610000027340; removedOnCompletion = 0;delegate = <UIViewAnimationState: 0x7fe69e0094e0>; fillMode = both; timingFunction = linear; duration = 2; toValue = 100; fromValue = 0.0; keyPath = uiFractionalProgress> 2 UIPacingAnimationForAnimatorsKey
  • 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.
  • 66.
  • 67.
    67 UIPacingAnimationForAnimatorsKey 2 x3 opacity 1 x3 •speed = 0 
 on 4 first • beginTime = -0.398438 on 2 last
  • 68.
    68 • By changingfractionComplete you spamming new CABasicAnimation objects
  • 69.
  • 70.
    70 • Receipt: • GoodDesigner 👑 • AfterEffects • Lottie + bodymovin • Done 🎉
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
    78 animationView = LOTAnimationView(name:"animation") self.view.addSubview(animationView) animationView.play() animationView.animationProgress = CGFloat(sender.value)
  • 79.
  • 80.
  • 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 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 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 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.
  • 86.
  • 87.
    87 • JSON • Size(in both ways) • Mutatablity • Control • Speed • Progress • Completion • All AfterEffects magic
  • 88.
  • 89.
    89 • Use CAShapeLayers •Combine multiply layers: • Use alpha/scale/mask/… • Use time-based animations
  • 90.
  • 91.
    91 Alexander Zimin –UIViewController, откройся!
  • 92.
  • 93.
    93 • Avoid time-basedanimations 🚀 • Implement user interactions (it’s easy 😉) • Create delegate for your custom animations on VCs 🔊 • Check github.com/azimin/AZTransitions 👍
  • 94.
  • 95.