lottie-ios 4.2.0 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/.github/workflows/main.yml +117 -11
  2. package/Gemfile +1 -0
  3. package/Gemfile.lock +5 -0
  4. package/Lottie.xcodeproj/project.pbxproj +1542 -194
  5. package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  6. package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (iOS).xcscheme +1 -1
  7. package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (macOS).xcscheme +1 -1
  8. package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (tvOS).xcscheme +4 -5
  9. package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (visionOS).xcscheme +66 -0
  10. package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +18 -0
  11. package/Lottie.xcworkspace/xcshareddata/swiftpm/Package.resolved +2 -11
  12. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  13. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +346 -102
  14. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +16 -5
  15. package/Package.resolved +2 -2
  16. package/Package.swift +16 -9
  17. package/README.md +2 -2
  18. package/Rakefile +122 -25
  19. package/Sources/Private/CoreAnimation/Animations/DropShadowAnimation.swift +160 -0
  20. package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +83 -10
  21. package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +85 -79
  22. package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +7 -7
  23. package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +67 -6
  24. package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +2 -2
  25. package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +9 -0
  26. package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +1 -0
  27. package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +1 -1
  28. package/Sources/Private/CoreAnimation/Layers/RepeaterLayer.swift +2 -0
  29. package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +11 -13
  30. package/Sources/Private/CoreAnimation/Layers/SolidLayer.swift +20 -5
  31. package/Sources/Private/CoreAnimation/Layers/TextLayer.swift +13 -3
  32. package/Sources/Private/CoreAnimation/ValueProviderStore.swift +6 -4
  33. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Collection+Diff.swift +263 -0
  34. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Diffable.swift +18 -0
  35. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/DiffableSection.swift +16 -0
  36. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/IndexChangeset.swift +187 -0
  37. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/SectionedChangeset.swift +32 -0
  38. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Logging/EpoxyLogger.swift +99 -0
  39. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/CallbackContextEpoxyModeled.swift +8 -0
  40. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelArrayBuilder.swift +48 -0
  41. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelProperty.swift +158 -0
  42. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelStorage.swift +88 -0
  43. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModeled.swift +54 -0
  44. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/AnyEpoxyModelProperty.swift +29 -0
  45. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/ClassReference.swift +39 -0
  46. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/AnimatedProviding.swift +10 -0
  47. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DataIDProviding.swift +57 -0
  48. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidDisplayProviding.swift +41 -0
  49. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidEndDisplayingProviding.swift +41 -0
  50. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidSelectProviding.swift +36 -0
  51. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ErasedContentProviding.swift +49 -0
  52. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/MakeViewProviding.swift +60 -0
  53. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetBehaviorsProviding.swift +38 -0
  54. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetContentProviding.swift +38 -0
  55. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/StyleIDProviding.swift +37 -0
  56. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/TraitCollectionProviding.swift +14 -0
  57. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewDifferentiatorProviding.swift +34 -0
  58. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewProviding.swift +13 -0
  59. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/WillDisplayProviding.swift +41 -0
  60. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/ViewEpoxyModeled.swift +10 -0
  61. package/Sources/Private/EmbeddedLibraries/EpoxyCore/README.md +31 -0
  62. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift +46 -0
  63. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingView.swift +391 -0
  64. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIIntrinsicContentSizeInvalidator.swift +44 -0
  65. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUILayoutMargins.swift +51 -0
  66. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift +172 -0
  67. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift +128 -0
  68. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift +452 -0
  69. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/SwiftUIView.swift +148 -0
  70. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift +40 -0
  71. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIViewConfiguringSwiftUIView.swift +43 -0
  72. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/BehaviorsConfigurableView.swift +45 -0
  73. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ContentConfigurableView.swift +36 -0
  74. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/EpoxyableView.swift +5 -0
  75. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/StyledView.swift +42 -0
  76. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ViewType.swift +51 -0
  77. package/Sources/Private/EmbeddedLibraries/README.md +27 -0
  78. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+BackingConfiguration.swift +4 -4
  79. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+MemoryFile.swift +2 -2
  80. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Writing.swift +4 -4
  81. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive.swift +8 -7
  82. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Compression.swift +5 -5
  83. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/FileManager+ZIP.swift +4 -4
  84. package/Sources/Private/MainThread/LayerContainers/CompLayers/ImageCompositionLayer.swift +6 -0
  85. package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +3 -1
  86. package/Sources/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.swift +18 -5
  87. package/Sources/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.swift +31 -3
  88. package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +33 -8
  89. package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +8 -1
  90. package/Sources/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.swift +13 -4
  91. package/Sources/Private/MainThread/LayerContainers/Utility/LayerFontProvider.swift +2 -2
  92. package/Sources/Private/MainThread/LayerContainers/Utility/LayerImageProvider.swift +1 -0
  93. package/Sources/Private/MainThread/LayerContainers/Utility/LayerTextProvider.swift +4 -4
  94. package/Sources/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +1 -1
  95. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift +3 -1
  96. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +2 -1
  97. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift +2 -1
  98. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +1 -1
  99. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +2 -2
  100. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +1 -1
  101. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +1 -1
  102. package/Sources/Private/MainThread/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +6 -2
  103. package/Sources/Private/Model/Assets/Asset.swift +8 -0
  104. package/Sources/Private/Model/Assets/AssetLibrary.swift +2 -2
  105. package/Sources/Private/Model/Assets/ImageAsset.swift +3 -3
  106. package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +16 -2
  107. package/Sources/Private/Model/DotLottie/DotLottieImageProvider.swift +20 -7
  108. package/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +26 -0
  109. package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +8 -5
  110. package/Sources/Private/Model/LayerEffects/DropShadowEffect.swift +45 -0
  111. package/Sources/Private/Model/LayerEffects/EffectValues/ColorEffectValue.swift +38 -0
  112. package/Sources/Private/Model/LayerEffects/EffectValues/EffectValue.swift +98 -0
  113. package/Sources/Private/Model/LayerEffects/EffectValues/Vector1DEffectValue.swift +38 -0
  114. package/Sources/Private/Model/LayerEffects/LayerEffect.swift +103 -0
  115. package/Sources/Private/Model/LayerStyles/DropShadowStyle.swift +72 -0
  116. package/Sources/Private/Model/LayerStyles/LayerStyle.swift +85 -0
  117. package/Sources/Private/Model/Layers/LayerModel.swift +27 -0
  118. package/Sources/Private/Model/Objects/Marker.swift +1 -1
  119. package/Sources/Private/Model/Objects/Transform.swift +1 -2
  120. package/Sources/Private/Model/ShapeItems/GradientFill.swift +1 -1
  121. package/Sources/Private/Model/ShapeItems/GradientStroke.swift +2 -2
  122. package/Sources/Private/Model/ShapeItems/Merge.swift +1 -1
  123. package/Sources/Private/Model/ShapeItems/ShapeItem.swift +31 -26
  124. package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +0 -9
  125. package/Sources/Private/Model/ShapeItems/Star.swift +1 -1
  126. package/Sources/Private/Model/Text/Font.swift +2 -2
  127. package/Sources/Private/Model/Text/Glyph.swift +1 -1
  128. package/Sources/Private/RootAnimationLayer.swift +2 -2
  129. package/Sources/Private/Utility/Debugging/LayerDebugging.swift +3 -1
  130. package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +32 -2
  131. package/Sources/Private/Utility/Helpers/AnimationContext.swift +4 -2
  132. package/Sources/Private/Utility/Helpers/AnyEquatable.swift +24 -0
  133. package/Sources/Private/Utility/Helpers/Binding+Map.swift +18 -0
  134. package/Sources/Private/Utility/Helpers/View+ValueChanged.swift +20 -0
  135. package/Sources/Private/Utility/LottieAnimationSource.swift +41 -0
  136. package/Sources/Private/Utility/Primitives/BezierPath.swift +2 -2
  137. package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +2 -2
  138. package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +1 -1
  139. package/Sources/Public/Animation/LottieAnimation.swift +21 -2
  140. package/Sources/Public/Animation/LottieAnimationLayer.swift +1464 -0
  141. package/Sources/Public/Animation/LottieAnimationView.swift +258 -735
  142. package/Sources/Public/Animation/LottiePlaybackMode.swift +117 -0
  143. package/Sources/Public/Animation/LottieView.swift +462 -0
  144. package/Sources/Public/AnimationCache/AnimationCacheProvider.swift +2 -1
  145. package/Sources/Public/AnimationCache/DefaultAnimationCache.swift +5 -6
  146. package/Sources/Public/Configuration/DecodingStrategy.swift +15 -0
  147. package/Sources/Public/Configuration/LottieConfiguration.swift +47 -0
  148. package/Sources/Public/Configuration/ReducedMotionOption.swift +114 -0
  149. package/Sources/Public/{LottieConfiguration.swift → Configuration/RenderingEngineOption.swift} +2 -57
  150. package/Sources/Public/{iOS → Controls}/AnimatedButton.swift +56 -13
  151. package/Sources/Public/{iOS → Controls}/AnimatedControl.swift +80 -8
  152. package/Sources/Public/{iOS → Controls}/AnimatedSwitch.swift +71 -31
  153. package/Sources/Public/Controls/LottieButton.swift +122 -0
  154. package/Sources/Public/Controls/LottieSwitch.swift +144 -0
  155. package/Sources/Public/Controls/LottieViewType.swift +79 -0
  156. package/Sources/Public/DotLottie/DotLottieConfiguration.swift +24 -0
  157. package/Sources/Public/DotLottie/DotLottieFile.swift +21 -6
  158. package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +79 -0
  159. package/Sources/Public/DynamicProperties/AnimationKeypath.swift +11 -1
  160. package/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +14 -0
  161. package/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +13 -0
  162. package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +22 -5
  163. package/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +14 -0
  164. package/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +13 -0
  165. package/Sources/Public/FontProvider/AnimationFontProvider.swift +8 -0
  166. package/Sources/Public/ImageProvider/AnimationImageProvider.swift +24 -0
  167. package/Sources/Public/Keyframes/Keyframe.swift +6 -0
  168. package/Sources/Public/Primitives/Vectors.swift +2 -2
  169. package/Sources/Public/TextProvider/AnimationTextProvider.swift +79 -6
  170. package/Sources/Public/iOS/AnimationSubview.swift +1 -1
  171. package/Sources/Public/iOS/BundleImageProvider.swift +16 -2
  172. package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +2 -2
  173. package/Sources/Public/iOS/FilepathImageProvider.swift +22 -3
  174. package/Sources/Public/iOS/LottieAnimationViewBase.swift +7 -1
  175. package/Sources/Public/macOS/BundleImageProvider.macOS.swift +15 -1
  176. package/Sources/Public/macOS/FilepathImageProvider.macOS.swift +21 -2
  177. package/lottie-ios.podspec +2 -1
  178. package/package.json +1 -1
  179. package/Sources/Private/Model/DotLottie/DotLottieConfiguration.swift +0 -15
  180. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Helpers.swift +0 -0
  181. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Progress.swift +0 -0
  182. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Reading.swift +0 -0
  183. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ReadingDeprecated.swift +0 -0
  184. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+WritingDeprecated.swift +0 -0
  185. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ZIP64.swift +0 -0
  186. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+CompressionDeprecated.swift +0 -0
  187. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Serialization.swift +0 -0
  188. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+Serialization.swift +0 -0
  189. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+ZIP64.swift +0 -0
  190. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry.swift +0 -0
  191. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/README.md +0 -0
  192. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/URL+ZIP.swift +0 -0
@@ -6,65 +6,79 @@
6
6
  //
7
7
 
8
8
  import Foundation
9
- #if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst)
9
+
10
+ #if canImport(UIKit)
10
11
  import UIKit
12
+ #elseif canImport(AppKit)
13
+ import AppKit
14
+ #endif
15
+
16
+ // MARK: - AnimatedSwitch
11
17
 
12
18
  /// An interactive switch with an 'On' and 'Off' state. When the user taps on the
13
19
  /// switch the state is toggled and the appropriate animation is played.
14
20
  ///
15
21
  /// Both the 'On' and 'Off' have an animation play range associated with their state.
22
+ ///
23
+ /// Also available as a SwiftUI view (`LottieSwitch`).
16
24
  open class AnimatedSwitch: AnimatedControl {
17
25
 
18
26
  // MARK: Lifecycle
19
27
 
20
28
  public override init(
21
- animation: LottieAnimation,
29
+ animation: LottieAnimation?,
22
30
  configuration: LottieConfiguration = .shared)
23
31
  {
24
32
  /// Generate a haptic generator if available.
25
33
  #if os(iOS)
26
- if #available(iOS 10.0, *) {
27
- self.hapticGenerator = HapticGenerator()
28
- } else {
29
- hapticGenerator = NullHapticGenerator()
30
- }
34
+ hapticGenerator = HapticGenerator()
31
35
  #else
32
36
  hapticGenerator = NullHapticGenerator()
33
37
  #endif
34
38
  super.init(animation: animation, configuration: configuration)
39
+
40
+ #if canImport(UIKit)
35
41
  isAccessibilityElement = true
42
+ #elseif canImport(AppKit)
43
+ setAccessibilityElement(true)
44
+ #endif
45
+
36
46
  updateOnState(isOn: _isOn, animated: false, shouldFireHaptics: false)
37
47
  }
38
48
 
39
49
  public override init() {
40
50
  /// Generate a haptic generator if available.
41
51
  #if os(iOS)
42
- if #available(iOS 10.0, *) {
43
- self.hapticGenerator = HapticGenerator()
44
- } else {
45
- hapticGenerator = NullHapticGenerator()
46
- }
52
+ hapticGenerator = HapticGenerator()
47
53
  #else
48
54
  hapticGenerator = NullHapticGenerator()
49
55
  #endif
56
+
50
57
  super.init()
58
+
59
+ #if canImport(UIKit)
51
60
  isAccessibilityElement = true
61
+ #elseif canImport(AppKit)
62
+ setAccessibilityElement(true)
63
+ #endif
64
+
52
65
  updateOnState(isOn: _isOn, animated: false, shouldFireHaptics: false)
53
66
  }
54
67
 
55
68
  required public init?(coder aDecoder: NSCoder) {
56
69
  /// Generate a haptic generator if available.
57
70
  #if os(iOS)
58
- if #available(iOS 10.0, *) {
59
- self.hapticGenerator = HapticGenerator()
60
- } else {
61
- hapticGenerator = NullHapticGenerator()
62
- }
71
+ hapticGenerator = HapticGenerator()
63
72
  #else
64
73
  hapticGenerator = NullHapticGenerator()
65
74
  #endif
66
75
  super.init(coder: aDecoder)
76
+
77
+ #if canImport(UIKit)
67
78
  isAccessibilityElement = true
79
+ #elseif canImport(AppKit)
80
+ setAccessibilityElement(true)
81
+ #endif
68
82
  }
69
83
 
70
84
  // MARK: Open
@@ -73,12 +87,23 @@ open class AnimatedSwitch: AnimatedControl {
73
87
  updateOnState(isOn: _isOn, animated: animateUpdateWhenChangingAnimation, shouldFireHaptics: false)
74
88
  }
75
89
 
90
+ #if canImport(UIKit)
76
91
  open override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
77
92
  super.endTracking(touch, with: event)
78
93
  updateOnState(isOn: !_isOn, animated: true, shouldFireHaptics: true)
79
94
  sendActions(for: .valueChanged)
80
95
  }
81
96
 
97
+ #elseif canImport(AppKit)
98
+ open override func handle(_ event: LottieNSControlEvent) {
99
+ super.handle(event)
100
+
101
+ if event == .touchUpInside {
102
+ updateOnState(isOn: !_isOn, animated: true, shouldFireHaptics: true)
103
+ }
104
+ }
105
+ #endif
106
+
82
107
  // MARK: Public
83
108
 
84
109
  /// Defines what happens when the user taps the switch while an
@@ -94,10 +119,15 @@ open class AnimatedSwitch: AnimatedControl {
94
119
  /// If `false` the switch will not play the animation when changing between animations.
95
120
  public var animateUpdateWhenChangingAnimation = true
96
121
 
122
+ #if canImport(UIKit)
97
123
  public override var accessibilityTraits: UIAccessibilityTraits {
98
124
  set { super.accessibilityTraits = newValue }
99
125
  get { super.accessibilityTraits.union(.button) }
100
126
  }
127
+ #endif
128
+
129
+ /// A closure that is called when the `isOn` state is updated
130
+ public var stateUpdated: ((_ isOn: Bool) -> Void)?
101
131
 
102
132
  /// The current state of the switch.
103
133
  public var isOn: Bool {
@@ -136,6 +166,11 @@ open class AnimatedSwitch: AnimatedControl {
136
166
 
137
167
  // MARK: Internal
138
168
 
169
+ private(set) var onStartProgress: CGFloat = 0
170
+ private(set) var onEndProgress: CGFloat = 1
171
+ private(set) var offStartProgress: CGFloat = 1
172
+ private(set) var offEndProgress: CGFloat = 0
173
+
139
174
  // MARK: Animation State
140
175
 
141
176
  func updateOnState(isOn: Bool, animated: Bool, shouldFireHaptics: Bool) {
@@ -188,21 +223,27 @@ open class AnimatedSwitch: AnimatedControl {
188
223
 
189
224
  // MARK: Fileprivate
190
225
 
191
- fileprivate var onStartProgress: CGFloat = 0
192
- fileprivate var onEndProgress: CGFloat = 1
193
- fileprivate var offStartProgress: CGFloat = 1
194
- fileprivate var offEndProgress: CGFloat = 0
195
- fileprivate var _isOn = false
196
226
  fileprivate var hapticGenerator: ImpactGenerator
197
227
 
228
+ fileprivate var _isOn = false {
229
+ didSet {
230
+ stateUpdated?(_isOn)
231
+ }
232
+ }
233
+
198
234
  // MARK: Private
199
235
 
200
236
  private func updateAccessibilityLabel() {
201
- accessibilityValue = _isOn ? NSLocalizedString("On", comment: "On") : NSLocalizedString("Off", comment: "Off")
237
+ let value = _isOn ? NSLocalizedString("On", comment: "On") : NSLocalizedString("Off", comment: "Off")
238
+
239
+ #if canImport(UIKit)
240
+ accessibilityValue = value
241
+ #elseif canImport(AppKit)
242
+ setAccessibilityValue(value)
243
+ #endif
202
244
  }
203
245
 
204
246
  }
205
- #endif
206
247
 
207
248
  // MARK: - ImpactGenerator
208
249
 
@@ -210,14 +251,7 @@ protocol ImpactGenerator {
210
251
  func generateImpact()
211
252
  }
212
253
 
213
- // MARK: - NullHapticGenerator
214
-
215
- class NullHapticGenerator: ImpactGenerator {
216
- func generateImpact() { }
217
- }
218
-
219
254
  #if os(iOS)
220
- @available(iOS 10.0, *)
221
255
  class HapticGenerator: ImpactGenerator {
222
256
 
223
257
  // MARK: Internal
@@ -230,4 +264,10 @@ class HapticGenerator: ImpactGenerator {
230
264
 
231
265
  fileprivate let impact = UIImpactFeedbackGenerator(style: .light)
232
266
  }
267
+ #else
268
+ // MARK: - NullHapticGenerator
269
+
270
+ class NullHapticGenerator: ImpactGenerator {
271
+ func generateImpact() { }
272
+ }
233
273
  #endif
@@ -0,0 +1,122 @@
1
+ // Created by Cal Stephens on 8/14/23.
2
+ // Copyright © 2023 Airbnb Inc. All rights reserved.
3
+
4
+ import SwiftUI
5
+
6
+ /// A wrapper which exposes Lottie's `AnimatedButton` to SwiftUI
7
+ @available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
8
+ public struct LottieButton: UIViewConfiguringSwiftUIView {
9
+
10
+ // MARK: Lifecycle
11
+
12
+ public init(animation: LottieAnimation?, action: @escaping () -> Void) {
13
+ self.animation = animation
14
+ self.action = action
15
+ }
16
+
17
+ // MARK: Public
18
+
19
+ public var body: some View {
20
+ AnimatedButton.swiftUIView {
21
+ let button = AnimatedButton(animation: animation, configuration: configuration)
22
+ button.performAction = action
23
+ return button
24
+ }
25
+ .configure { context in
26
+ // We check referential equality of the animation before updating as updating the
27
+ // animation has a side-effect of rebuilding the animation layer, and it would be
28
+ // prohibitive to do so on every state update.
29
+ if animation !== context.view.animationView.animation {
30
+ context.view.animationView.animation = animation
31
+ }
32
+
33
+ #if os(macOS)
34
+ // Disable the intrinsic content size constraint on the inner animation view,
35
+ // or the Epoxy `SwiftUIMeasurementContainer` won't size this view correctly.
36
+ context.view.animationView.isVerticalContentSizeConstraintActive = false
37
+ context.view.animationView.isHorizontalContentSizeConstraintActive = false
38
+ #endif
39
+ }
40
+ .configurations(configurations)
41
+ }
42
+
43
+ /// Returns a copy of this `LottieView` updated to have the given closure applied to its
44
+ /// represented `LottieAnimationView` whenever it is updated via the `updateUIView(…)`
45
+ /// or `updateNSView(…)` method.
46
+ public func configure(_ configure: @escaping (AnimatedButton) -> Void) -> Self {
47
+ var copy = self
48
+ copy.configurations.append { context in
49
+ configure(context.view)
50
+ }
51
+ return copy
52
+ }
53
+
54
+ /// Returns a copy of this view with its `LottieConfiguration` updated to the given value.
55
+ public func configuration(_ configuration: LottieConfiguration) -> Self {
56
+ var copy = self
57
+ copy.configuration = configuration
58
+
59
+ copy = copy.configure { view in
60
+ if view.animationView.configuration != configuration {
61
+ view.animationView.configuration = configuration
62
+ }
63
+ }
64
+
65
+ return copy
66
+ }
67
+
68
+ /// Returns a copy of this view configured to animate between the
69
+ /// given progress values when the given event is triggered
70
+ public func animate(
71
+ fromProgress: AnimationProgressTime,
72
+ toProgress: AnimationProgressTime,
73
+ on event: LottieControlEvent)
74
+ -> Self
75
+ {
76
+ configure { view in
77
+ // `setPlayRange` just modifies a dictionary,
78
+ // so we can just call it on every state update without diffing
79
+ view.setPlayRange(fromProgress: fromProgress, toProgress: toProgress, event: event)
80
+ }
81
+ }
82
+
83
+ /// Returns a copy of this view configured to animate between the
84
+ /// given markers when the given event is triggered
85
+ public func animate(
86
+ fromMarker: String,
87
+ toMarker: String,
88
+ on event: LottieControlEvent)
89
+ -> Self
90
+ {
91
+ configure { view in
92
+ // `setPlayRange` just modifies a dictionary,
93
+ // so we can just call it on every state update without diffing
94
+ view.setPlayRange(fromMarker: fromMarker, toMarker: toMarker, event: event)
95
+ }
96
+ }
97
+
98
+ /// Returns a copy of this view using the given value provider for the given keypath.
99
+ /// The value provider must be `Equatable` to avoid unnecessary state updates / re-renders.
100
+ public func valueProvider<ValueProvider: AnyValueProvider & Equatable>(
101
+ _ valueProvider: ValueProvider,
102
+ for keypath: AnimationKeypath)
103
+ -> Self
104
+ {
105
+ configure { view in
106
+ if (view.animationView.valueProviders[keypath] as? ValueProvider) != valueProvider {
107
+ view.animationView.setValueProvider(valueProvider, keypath: keypath)
108
+ }
109
+ }
110
+ }
111
+
112
+ // MARK: Internal
113
+
114
+ var configurations = [SwiftUIView<AnimatedButton, Void>.Configuration]()
115
+
116
+ // MARK: Private
117
+
118
+ private let animation: LottieAnimation?
119
+ private let action: () -> Void
120
+ private var configuration: LottieConfiguration = .shared
121
+
122
+ }
@@ -0,0 +1,144 @@
1
+ // Created by Cal Stephens on 8/11/23.
2
+ // Copyright © 2023 Airbnb Inc. All rights reserved.
3
+
4
+ import SwiftUI
5
+
6
+ /// A wrapper which exposes Lottie's `AnimatedSwitch` to SwiftUI
7
+ @available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
8
+ public struct LottieSwitch: UIViewConfiguringSwiftUIView {
9
+
10
+ // MARK: Lifecycle
11
+
12
+ public init(animation: LottieAnimation?) {
13
+ self.animation = animation
14
+ }
15
+
16
+ // MARK: Public
17
+
18
+ public var body: some View {
19
+ AnimatedSwitch.swiftUIView {
20
+ let animatedSwitch = AnimatedSwitch(animation: animation, configuration: configuration)
21
+ animatedSwitch.isOn = isOn?.wrappedValue ?? false
22
+ return animatedSwitch
23
+ }
24
+ .configure { context in
25
+ // We check referential equality of the animation before updating as updating the
26
+ // animation has a side-effect of rebuilding the animation layer, and it would be
27
+ // prohibitive to do so on every state update.
28
+ if animation !== context.view.animationView.animation {
29
+ context.view.animationView.animation = animation
30
+ }
31
+
32
+ #if os(macOS)
33
+ // Disable the intrinsic content size constraint on the inner animation view,
34
+ // or the Epoxy `SwiftUIMeasurementContainer` won't size this view correctly.
35
+ context.view.animationView.isVerticalContentSizeConstraintActive = false
36
+ context.view.animationView.isHorizontalContentSizeConstraintActive = false
37
+ #endif
38
+
39
+ if let isOn = isOn?.wrappedValue, isOn != context.view.isOn {
40
+ context.view.setIsOn(isOn, animated: true)
41
+ }
42
+ }
43
+ .configurations(configurations)
44
+ }
45
+
46
+ /// Returns a copy of this `LottieView` updated to have the given closure applied to its
47
+ /// represented `LottieAnimationView` whenever it is updated via the `updateUIView(…)`
48
+ /// or `updateNSView(…)` method.
49
+ public func configure(_ configure: @escaping (AnimatedSwitch) -> Void) -> Self {
50
+ var copy = self
51
+ copy.configurations.append { context in
52
+ configure(context.view)
53
+ }
54
+ return copy
55
+ }
56
+
57
+ /// Returns a copy of this view with its `LottieConfiguration` updated to the given value.
58
+ public func configuration(_ configuration: LottieConfiguration) -> Self {
59
+ var copy = self
60
+ copy.configuration = configuration
61
+
62
+ copy = copy.configure { view in
63
+ if view.animationView.configuration != configuration {
64
+ view.animationView.configuration = configuration
65
+ }
66
+ }
67
+
68
+ return copy
69
+ }
70
+
71
+ /// Returns a copy of this view with the given `Binding` reflecting the `isOn` state of the switch.
72
+ public func isOn(_ binding: Binding<Bool>) -> Self {
73
+ var copy = self
74
+ copy.isOn = binding
75
+ return copy.configure { view in
76
+ view.stateUpdated = { isOn in
77
+ DispatchQueue.main.async {
78
+ binding.wrappedValue = isOn
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ /// Returns a copy of this view with the "on" animation configured
85
+ /// to start and end at the given progress values.
86
+ /// Defaults to playing the entire animation forwards (0...1).
87
+ public func onAnimation(
88
+ fromProgress onStartProgress: AnimationProgressTime,
89
+ toProgress onEndProgress: AnimationProgressTime)
90
+ -> Self
91
+ {
92
+ configure { view in
93
+ if onStartProgress != view.onStartProgress || onEndProgress != view.onEndProgress {
94
+ view.setProgressForState(
95
+ fromProgress: onStartProgress,
96
+ toProgress: onEndProgress,
97
+ forOnState: true)
98
+ }
99
+ }
100
+ }
101
+
102
+ /// Returns a copy of this view with the "on" animation configured
103
+ /// to start and end at the given progress values.
104
+ /// Defaults to playing the entire animation backwards (1...0).
105
+ public func offAnimation(
106
+ fromProgress offStartProgress: AnimationProgressTime,
107
+ toProgress offEndProgress: AnimationProgressTime)
108
+ -> Self
109
+ {
110
+ configure { view in
111
+ if offStartProgress != view.offStartProgress || offEndProgress != view.offEndProgress {
112
+ view.setProgressForState(
113
+ fromProgress: offStartProgress,
114
+ toProgress: offEndProgress,
115
+ forOnState: false)
116
+ }
117
+ }
118
+ }
119
+
120
+ /// Returns a copy of this view using the given value provider for the given keypath.
121
+ /// The value provider must be `Equatable` to avoid unnecessary state updates / re-renders.
122
+ public func valueProvider<ValueProvider: AnyValueProvider & Equatable>(
123
+ _ valueProvider: ValueProvider,
124
+ for keypath: AnimationKeypath)
125
+ -> Self
126
+ {
127
+ configure { view in
128
+ if (view.animationView.valueProviders[keypath] as? ValueProvider) != valueProvider {
129
+ view.animationView.setValueProvider(valueProvider, keypath: keypath)
130
+ }
131
+ }
132
+ }
133
+
134
+ // MARK: Internal
135
+
136
+ var configurations = [SwiftUIView<AnimatedSwitch, Void>.Configuration]()
137
+
138
+ // MARK: Private
139
+
140
+ private let animation: LottieAnimation?
141
+ private var configuration: LottieConfiguration = .shared
142
+ private var isOn: Binding<Bool>?
143
+
144
+ }
@@ -0,0 +1,79 @@
1
+ // Created by Cal Stephens on 8/11/23.
2
+ // Copyright © 2023 Airbnb Inc. All rights reserved.
3
+
4
+ #if canImport(UIKit)
5
+ import UIKit
6
+
7
+ /// The control base type for this platform.
8
+ /// - `UIControl` on iOS / tvOS and `NSControl` on macOS.
9
+ public typealias LottieControlType = UIControl
10
+
11
+ /// The `State` type of `LottieControlType`
12
+ /// - `UIControl.State` on iOS / tvOS and `NSControl.StateValue` on macOS.
13
+ public typealias LottieControlState = UIControl.State
14
+
15
+ /// The event type handled by the `LottieControlType` component for this platform.
16
+ /// - `UIControl.Event` on iOS / tvOS and `LottieNSControlEvent` on macOS.
17
+ public typealias LottieControlEvent = UIControl.Event
18
+
19
+ extension LottieControlEvent {
20
+ var id: AnyHashable {
21
+ rawValue
22
+ }
23
+ }
24
+ #else
25
+ import AppKit
26
+
27
+ /// The control base type for this platform.
28
+ /// - `UIControl` on iOS / tvOS and `NSControl` on macOS.
29
+ public typealias LottieControlType = NSControl
30
+
31
+ /// The `State` type of `LottieControlType`
32
+ /// - `UIControl.State` on iOS / tvOS and `NSControl.StateValue` on macOS.
33
+ public typealias LottieControlState = LottieNSControlState
34
+
35
+ /// AppKit equivalent of `UIControl.State` for `AnimatedControl`
36
+ public enum LottieNSControlState: UInt, RawRepresentable {
37
+ /// The normal, or default, state of a control where the control is enabled but neither selected nor highlighted.
38
+ case normal
39
+ /// The highlighted state of a control.
40
+ case highlighted
41
+ }
42
+
43
+ /// The event type handled by the `LottieControlType` component for this platform.
44
+ /// - `UIControl.Event` on iOS / tvOS and `LottieNSControlEvent` on macOS.
45
+ public typealias LottieControlEvent = LottieNSControlEvent
46
+
47
+ public struct LottieNSControlEvent: Equatable {
48
+
49
+ // MARK: Lifecycle
50
+
51
+ public init(_ event: NSEvent.EventType, inside: Bool) {
52
+ self.event = event
53
+ self.inside = inside
54
+ }
55
+
56
+ // MARK: Public
57
+
58
+ /// macOS equivalent to `UIControl.Event.touchDown`
59
+ public static let touchDown = LottieNSControlEvent(.leftMouseDown, inside: true)
60
+
61
+ /// macOS equivalent to `UIControl.Event.touchUpInside`
62
+ public static let touchUpInside = LottieNSControlEvent(.leftMouseUp, inside: true)
63
+
64
+ /// macOS equivalent to `UIControl.Event.touchUpInside`
65
+ public static let touchUpOutside = LottieNSControlEvent(.leftMouseUp, inside: false)
66
+
67
+ /// The underlying `NSEvent.EventType` of this event, which is roughly equivalent to `UIControl.Event`
68
+ public var event: NSEvent.EventType
69
+
70
+ /// Whether or not the mouse must be inside the control.
71
+ public var inside: Bool
72
+
73
+ // MARK: Internal
74
+
75
+ var id: AnyHashable {
76
+ [AnyHashable(event.rawValue), AnyHashable(inside)]
77
+ }
78
+ }
79
+ #endif
@@ -0,0 +1,24 @@
1
+ //
2
+ // DotLottieConfiguration.swift
3
+ // Lottie
4
+ //
5
+ // Created by Evandro Hoffmann on 19/10/22.
6
+ //
7
+
8
+ // MARK: - DotLottieConfiguration
9
+
10
+ /// The `DotLottieConfiguration` model holds the presets extracted from DotLottieAnimation
11
+ /// The presets are used as input to setup `LottieAnimationView` before playing the animation.
12
+ public struct DotLottieConfiguration {
13
+ /// id of the animation
14
+ public var id: String
15
+
16
+ /// Animation Image Provider
17
+ public var imageProvider: AnimationImageProvider?
18
+
19
+ /// Loop behaviour of animation
20
+ public var loopMode: LottieLoopMode
21
+
22
+ /// Playback speed of animation
23
+ public var speed: Double
24
+ }
@@ -25,19 +25,21 @@ public final class DotLottieFile {
25
25
  try decompress(data: data, to: fileUrl)
26
26
  }
27
27
 
28
- // MARK: Internal
28
+ // MARK: Public
29
29
 
30
30
  /// Definition for a single animation within a `DotLottieFile`
31
- struct Animation {
32
- let animation: LottieAnimation
33
- let configuration: DotLottieConfiguration
31
+ public struct Animation {
32
+ public let animation: LottieAnimation
33
+ public let configuration: DotLottieConfiguration
34
34
  }
35
35
 
36
36
  /// List of `LottieAnimation` in the file
37
- private(set) var animations: [Animation] = []
37
+ public private(set) var animations: [Animation] = []
38
+
39
+ // MARK: Internal
38
40
 
39
41
  /// Image provider for animations
40
- private(set) var imageProvider: AnimationImageProvider?
42
+ private(set) var imageProvider: DotLottieImageProvider?
41
43
 
42
44
  /// Animations folder url
43
45
  lazy var animationsUrl: URL = fileUrl.appendingPathComponent("\(DotLottieFile.animationsFolderName)")
@@ -60,6 +62,12 @@ public final class DotLottieFile {
60
62
  }
61
63
  }
62
64
 
65
+ /// The `LottieAnimation` and `DotLottieConfiguration` for the given animation index in this file
66
+ func animation(at index: Int) -> DotLottieFile.Animation? {
67
+ guard index < animations.count else { return nil }
68
+ return animations[index]
69
+ }
70
+
63
71
  // MARK: Private
64
72
 
65
73
  private static let manifestFileName = "manifest.json"
@@ -136,3 +144,10 @@ extension String {
136
144
  (self as NSString).deletingPathExtension
137
145
  }
138
146
  }
147
+
148
+ // MARK: - DotLottieFile + Sendable
149
+
150
+ // Mark `DotLottieFile` as `@unchecked Sendable` to allow it to be used when strict concurrency is enabled.
151
+ // In the future, it may be necessary to make changes to the internal implementation of `DotLottieFile`
152
+ // to make it truly thread-safe.
153
+ extension DotLottieFile: @unchecked Sendable { }