-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
/
AnimatedControl.swift
156 lines (126 loc) · 4.26 KB
/
AnimatedControl.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//
// AnimatedControl.swift
// lottie-swift
//
// Created by Brandon Withrow on 2/4/19.
//
import Foundation
import UIKit
/**
Lottie comes prepacked with a two Animated Controls, `AnimatedSwitch` and
`AnimatedButton`. Both of these controls are built on top of `AnimatedControl`
`AnimatedControl` is a subclass of `UIControl` that provides an interactive
mechanism for controlling the visual state of an animation in response to
user actions.
The `AnimatedControl` will show and hide layers depending on the current
`UIControl.State` of the control.
Users of `AnimationControl` can set a Layer Name for each `UIControl.State`.
When the state is change the `AnimationControl` will change the visibility
of its layers.
NOTE: Do not initialize directly. This is intended to be subclassed.
*/
open class AnimatedControl: UIControl {
// MARK: Public
/// The animation backing the animated control.
public var animation: Animation? {
didSet {
animationView.animation = animation
animationView.bounds = animation?.bounds ?? .zero
setNeedsLayout()
updateForState()
animationDidSet()
}
}
/// Sets which Animation Layer should be visible for the given state.
public func setLayer(named: String, forState: UIControl.State) {
stateMap[forState.rawValue] = named
updateForState()
}
/// Sets a ValueProvider for the specified keypath
public func setValueProvider(_ valueProvider: AnyValueProvider, keypath: AnimationKeypath) {
animationView.setValueProvider(valueProvider, keypath: keypath)
}
// MARK: Initializers
public init(animation: Animation) {
self.animationView = AnimationView(animation: animation)
super.init(frame: animation.bounds)
commonInit()
}
public init() {
self.animationView = AnimationView()
super.init(frame: .zero)
commonInit()
}
required public init?(coder aDecoder: NSCoder) {
self.animationView = AnimationView()
super.init(coder: aDecoder)
commonInit()
}
// MARK: UIControl Overrides
open override var isEnabled: Bool {
didSet {
updateForState()
}
}
open override var isSelected: Bool {
didSet {
updateForState()
}
}
open override var isHighlighted: Bool {
didSet {
updateForState()
}
}
open override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
updateForState()
return super.beginTracking(touch, with: event)
}
open override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
updateForState()
return super.continueTracking(touch, with: event)
}
open override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
updateForState()
return super.endTracking(touch, with: event)
}
open override func cancelTracking(with event: UIEvent?) {
updateForState()
super.cancelTracking(with: event)
}
open override var intrinsicContentSize: CGSize {
return animationView.intrinsicContentSize
}
open func animationDidSet() {
}
// MARK: Private
let animationView: AnimationView
var stateMap: [UInt : String] = [:]
fileprivate func commonInit() {
animationView.clipsToBounds = false
clipsToBounds = true
animationView.translatesAutoresizingMaskIntoConstraints = false
animationView.backgroundBehavior = .forceFinish
addSubview(animationView)
animationView.contentMode = .scaleAspectFit
animationView.isUserInteractionEnabled = false
animationView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
animationView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
animationView.topAnchor.constraint(equalTo: topAnchor).isActive = true
animationView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
func updateForState() {
guard let animationLayer = animationView.animationLayer else { return }
if let layerName = stateMap[state.rawValue],
let stateLayer = animationLayer.layer(for: AnimationKeypath(keypath: layerName)) {
for layer in animationLayer.animationLayers {
layer.isHidden = true
}
stateLayer.isHidden = false
} else {
for layer in animationLayer.animationLayers {
layer.isHidden = false
}
}
}
}