lottie-ios 4.3.3 → 4.4.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 (199) hide show
  1. package/.github/workflows/main.yml +51 -58
  2. package/Lottie.xcodeproj/project.pbxproj +40 -0
  3. package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +4 -17
  4. package/Lottie.xcworkspace/xcshareddata/swiftpm/Package.resolved +37 -40
  5. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  6. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +9 -666
  7. package/Package.resolved +20 -22
  8. package/Package.swift +4 -2
  9. package/README.md +21 -3
  10. package/Rakefile +52 -28
  11. package/Sources/PrivacyInfo.xcprivacy +23 -0
  12. package/Sources/Private/CoreAnimation/Animations/CAAnimation+TimingConfiguration.swift +1 -1
  13. package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +23 -13
  14. package/Sources/Private/CoreAnimation/Animations/CustomPathAnimation.swift +8 -2
  15. package/Sources/Private/CoreAnimation/Animations/EllipseAnimation.swift +7 -1
  16. package/Sources/Private/CoreAnimation/Animations/GradientAnimations.swift +15 -2
  17. package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +8 -1
  18. package/Sources/Private/CoreAnimation/Animations/RectangleAnimation.swift +8 -1
  19. package/Sources/Private/CoreAnimation/Animations/StarAnimation.swift +12 -1
  20. package/Sources/Private/CoreAnimation/Animations/StrokeAnimation.swift +0 -1
  21. package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +19 -11
  22. package/Sources/Private/CoreAnimation/Animations/VisibilityAnimation.swift +45 -19
  23. package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +38 -3
  24. package/Sources/Private/CoreAnimation/Extensions/CALayer+fillBounds.swift +1 -1
  25. package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +9 -2
  26. package/Sources/Private/CoreAnimation/Extensions/Keyframes+timeRemapping.swift +46 -0
  27. package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +77 -13
  28. package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +1 -1
  29. package/Sources/Private/CoreAnimation/Layers/CALayer+setupLayerHierarchy.swift +2 -2
  30. package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +1 -1
  31. package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +1 -7
  32. package/Sources/Private/CoreAnimation/Layers/PreCompLayer.swift +19 -53
  33. package/Sources/Private/CoreAnimation/Layers/ShapeItemLayer.swift +2 -2
  34. package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +97 -31
  35. package/Sources/Private/CoreAnimation/Layers/SolidLayer.swift +5 -2
  36. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelArrayBuilder.swift +1 -1
  37. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelProperty.swift +10 -10
  38. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift +1 -1
  39. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingView.swift +1 -3
  40. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIIntrinsicContentSizeInvalidator.swift +2 -0
  41. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUILayoutMargins.swift +2 -0
  42. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift +2 -0
  43. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift +2 -0
  44. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift +2 -0
  45. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/SwiftUIView.swift +2 -0
  46. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift +2 -0
  47. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIViewConfiguringSwiftUIView.swift +2 -0
  48. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ViewType.swift +2 -1
  49. package/Sources/Private/EmbeddedLibraries/LRUCache/LRUCache.swift +256 -0
  50. package/Sources/Private/EmbeddedLibraries/LRUCache/README.md +24 -0
  51. package/Sources/Private/EmbeddedLibraries/README.md +16 -0
  52. package/Sources/Private/EmbeddedLibraries/ZipFoundation/Archive+Helpers.swift +2 -2
  53. package/Sources/Private/EmbeddedLibraries/ZipFoundation/Archive+MemoryFile.swift +7 -7
  54. package/Sources/Private/EmbeddedLibraries/ZipFoundation/Data+Compression.swift +2 -2
  55. package/Sources/Private/EmbeddedLibraries/ZipFoundation/FileManager+ZIP.swift +5 -5
  56. package/Sources/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.swift +1 -2
  57. package/Sources/Private/MainThread/LayerContainers/CompLayers/ImageCompositionLayer.swift +1 -3
  58. package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +1 -2
  59. package/Sources/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.swift +2 -3
  60. package/Sources/Private/MainThread/LayerContainers/CompLayers/SolidCompositionLayer.swift +2 -3
  61. package/Sources/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.swift +5 -12
  62. package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +0 -1
  63. package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +11 -7
  64. package/Sources/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.swift +3 -3
  65. package/Sources/Private/MainThread/LayerContainers/Utility/CoreTextRenderLayer.swift +12 -12
  66. package/Sources/Private/MainThread/LayerContainers/Utility/InvertedMatteLayer.swift +1 -3
  67. package/Sources/Private/MainThread/LayerContainers/Utility/LayerFontProvider.swift +0 -2
  68. package/Sources/Private/MainThread/LayerContainers/Utility/LayerImageProvider.swift +1 -3
  69. package/Sources/Private/MainThread/LayerContainers/Utility/LayerTextProvider.swift +0 -2
  70. package/Sources/Private/MainThread/LayerContainers/Utility/LayerTransformNode.swift +0 -2
  71. package/Sources/Private/MainThread/NodeRenderSystem/Extensions/ItemsExtension.swift +1 -3
  72. package/Sources/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift +0 -1
  73. package/Sources/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +0 -1
  74. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift +1 -3
  75. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift +1 -2
  76. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift +0 -1
  77. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift +0 -2
  78. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +0 -1
  79. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift +0 -1
  80. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift +0 -1
  81. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift +2 -3
  82. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +1 -3
  83. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +2 -2
  84. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +0 -2
  85. package/Sources/Private/MainThread/NodeRenderSystem/Protocols/AnimatorNode.swift +0 -1
  86. package/Sources/Private/MainThread/NodeRenderSystem/Protocols/PathNode.swift +0 -2
  87. package/Sources/Private/MainThread/NodeRenderSystem/Protocols/RenderNode.swift +0 -2
  88. package/Sources/Private/MainThread/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +0 -1
  89. package/Sources/Private/MainThread/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift +0 -1
  90. package/Sources/Private/Model/Assets/Asset.swift +2 -3
  91. package/Sources/Private/Model/Assets/AssetLibrary.swift +11 -11
  92. package/Sources/Private/Model/Assets/ImageAsset.swift +20 -0
  93. package/Sources/Private/Model/Assets/PrecompAsset.swift +0 -2
  94. package/Sources/Private/Model/DictionaryInitializable.swift +18 -10
  95. package/Sources/Private/Model/DotLottie/DotLottieImageProvider.swift +9 -6
  96. package/Sources/Private/Model/DotLottie/DotLottieUtils.swift +1 -1
  97. package/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +5 -4
  98. package/Sources/Private/Model/Keyframes/KeyframeData.swift +1 -4
  99. package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +1 -3
  100. package/Sources/Private/Model/LayerEffects/DropShadowEffect.swift +0 -2
  101. package/Sources/Private/Model/LayerEffects/EffectValues/ColorEffectValue.swift +0 -2
  102. package/Sources/Private/Model/LayerEffects/EffectValues/EffectValue.swift +4 -5
  103. package/Sources/Private/Model/LayerEffects/EffectValues/Vector1DEffectValue.swift +0 -2
  104. package/Sources/Private/Model/LayerEffects/LayerEffect.swift +2 -3
  105. package/Sources/Private/Model/LayerStyles/DropShadowStyle.swift +0 -2
  106. package/Sources/Private/Model/LayerStyles/LayerStyle.swift +2 -3
  107. package/Sources/Private/Model/Layers/ImageLayerModel.swift +0 -2
  108. package/Sources/Private/Model/Layers/LayerModel.swift +16 -5
  109. package/Sources/Private/Model/Layers/PreCompLayerModel.swift +0 -2
  110. package/Sources/Private/Model/Layers/ShapeLayerModel.swift +0 -2
  111. package/Sources/Private/Model/Layers/SolidLayerModel.swift +0 -2
  112. package/Sources/Private/Model/Layers/TextLayerModel.swift +0 -2
  113. package/Sources/Private/Model/Objects/DashPattern.swift +1 -3
  114. package/Sources/Private/Model/Objects/Marker.swift +0 -2
  115. package/Sources/Private/Model/Objects/Mask.swift +0 -2
  116. package/Sources/Private/Model/Objects/Transform.swift +46 -9
  117. package/Sources/Private/Model/ShapeItems/Ellipse.swift +0 -2
  118. package/Sources/Private/Model/ShapeItems/Fill.swift +0 -2
  119. package/Sources/Private/Model/ShapeItems/GradientFill.swift +1 -3
  120. package/Sources/Private/Model/ShapeItems/GradientStroke.swift +1 -3
  121. package/Sources/Private/Model/ShapeItems/Group.swift +0 -2
  122. package/Sources/Private/Model/ShapeItems/Merge.swift +1 -3
  123. package/Sources/Private/Model/ShapeItems/Rectangle.swift +0 -2
  124. package/Sources/Private/Model/ShapeItems/Repeater.swift +0 -2
  125. package/Sources/Private/Model/ShapeItems/RoundedCorners.swift +0 -2
  126. package/Sources/Private/Model/ShapeItems/Shape.swift +0 -2
  127. package/Sources/Private/Model/ShapeItems/ShapeItem.swift +2 -3
  128. package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +0 -2
  129. package/Sources/Private/Model/ShapeItems/Star.swift +1 -3
  130. package/Sources/Private/Model/ShapeItems/Stroke.swift +0 -2
  131. package/Sources/Private/Model/ShapeItems/Trim.swift +1 -3
  132. package/Sources/Private/Model/Text/Font.swift +0 -2
  133. package/Sources/Private/Model/Text/Glyph.swift +0 -2
  134. package/Sources/Private/Model/Text/TextAnimator.swift +0 -2
  135. package/Sources/Private/Model/Text/TextDocument.swift +2 -4
  136. package/Sources/Private/Utility/Debugging/AnimatorNodeDebugging.swift +0 -2
  137. package/Sources/Private/Utility/Debugging/LayerDebugging.swift +4 -5
  138. package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +2 -3
  139. package/Sources/Private/Utility/Extensions/DataExtension.swift +0 -1
  140. package/Sources/Private/Utility/Extensions/StringExtensions.swift +5 -0
  141. package/Sources/Private/Utility/Helpers/AnimationContext.swift +2 -3
  142. package/Sources/Private/Utility/Helpers/AnyEquatable.swift +0 -2
  143. package/Sources/Private/Utility/Helpers/Binding+Map.swift +2 -0
  144. package/Sources/Private/Utility/Helpers/View+ValueChanged.swift +2 -0
  145. package/Sources/Private/Utility/LottieAnimationSource.swift +11 -1
  146. package/Sources/Private/Utility/Primitives/BezierPath.swift +2 -3
  147. package/Sources/Private/Utility/Primitives/CGPointExtension.swift +1 -1
  148. package/Sources/Private/Utility/Primitives/ColorExtension.swift +1 -2
  149. package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +6 -6
  150. package/Sources/Public/Animation/LottieAnimation.swift +2 -2
  151. package/Sources/Public/Animation/LottieAnimationHelpers.swift +7 -5
  152. package/Sources/Public/Animation/LottieAnimationLayer.swift +30 -43
  153. package/Sources/Public/Animation/LottieAnimationView.swift +18 -8
  154. package/Sources/Public/Animation/LottieAnimationViewInitializers.swift +3 -3
  155. package/Sources/Public/Animation/LottiePlaybackMode.swift +53 -0
  156. package/Sources/Public/Animation/LottieView.swift +145 -48
  157. package/Sources/Public/AnimationCache/AnimationCacheProvider.swift +0 -2
  158. package/Sources/Public/AnimationCache/DefaultAnimationCache.swift +21 -7
  159. package/Sources/Public/AnimationCache/LRUAnimationCache.swift +0 -2
  160. package/Sources/Public/Controls/AnimatedButton.swift +4 -6
  161. package/Sources/Public/Controls/AnimatedControl.swift +1 -3
  162. package/Sources/Public/Controls/AnimatedSwitch.swift +1 -3
  163. package/Sources/Public/Controls/LottieButton.swift +2 -1
  164. package/Sources/Public/Controls/LottieSwitch.swift +2 -0
  165. package/Sources/Public/DotLottie/Cache/DotLottieCache.swift +17 -4
  166. package/Sources/Public/DotLottie/Cache/DotLottieCacheProvider.swift +1 -3
  167. package/Sources/Public/DotLottie/DotLottieConfiguration.swift +53 -5
  168. package/Sources/Public/DotLottie/DotLottieFile.swift +4 -3
  169. package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +18 -11
  170. package/Sources/Public/DynamicProperties/AnimationKeypath.swift +0 -2
  171. package/Sources/Public/DynamicProperties/AnyValueProvider.swift +0 -1
  172. package/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +2 -2
  173. package/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +1 -1
  174. package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +1 -1
  175. package/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +1 -1
  176. package/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +1 -1
  177. package/Sources/Public/FontProvider/AnimationFontProvider.swift +0 -2
  178. package/Sources/Public/ImageProvider/AnimationImageProvider.swift +0 -2
  179. package/Sources/Public/Keyframes/Interpolatable.swift +26 -0
  180. package/Sources/Public/Primitives/LottieColor.swift +0 -2
  181. package/Sources/Public/Primitives/Vectors.swift +0 -2
  182. package/Sources/Public/TextProvider/AnimationTextProvider.swift +0 -2
  183. package/Sources/Public/iOS/AnimationSubview.swift +0 -1
  184. package/Sources/Public/iOS/BundleImageProvider.swift +3 -8
  185. package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +4 -8
  186. package/Sources/Public/iOS/LottieAnimationViewBase.swift +5 -2
  187. package/Sources/Public/iOS/UIColorExtension.swift +1 -2
  188. package/Sources/Public/macOS/BundleImageProvider.macOS.swift +3 -6
  189. package/lottie-ios.podspec +6 -2
  190. package/package.json +1 -1
  191. package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  192. package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  193. package/Lottie.xcodeproj/xcuserdata/cal.xcuserdatad/xcschemes/xcschememanagement.plist +0 -37
  194. package/Lottie.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  195. package/Lottie.xcworkspace/xcuserdata/cal.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
  196. package/Lottie.xcworkspace/xcuserdata/cal.xcuserdatad/xcdebugger/Expressions.xcexplist +0 -153
  197. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/IDEFindNavigatorScopes.plist +0 -5
  198. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +0 -231
  199. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +0 -37
package/Package.resolved CHANGED
@@ -1,25 +1,23 @@
1
1
  {
2
- "object": {
3
- "pins": [
4
- {
5
- "package": "AirbnbSwift",
6
- "repositoryURL": "https://github.com/airbnb/swift",
7
- "state": {
8
- "branch": null,
9
- "revision": "b408d36b4f5e73ea75441fb9791b849b0a40f58b",
10
- "version": "1.0.5"
11
- }
12
- },
13
- {
14
- "package": "swift-argument-parser",
15
- "repositoryURL": "https://github.com/apple/swift-argument-parser",
16
- "state": {
17
- "branch": null,
18
- "revision": "df9ee6676cd5b3bf5b330ec7568a5644f547201b",
19
- "version": "1.1.3"
20
- }
2
+ "pins" : [
3
+ {
4
+ "identity" : "swift",
5
+ "kind" : "remoteSourceControl",
6
+ "location" : "https://github.com/airbnb/swift",
7
+ "state" : {
8
+ "revision" : "bc6aa7c3e21b6ab951ce75afc0a6e6d16fd6caef",
9
+ "version" : "1.0.6"
21
10
  }
22
- ]
23
- },
24
- "version": 1
11
+ },
12
+ {
13
+ "identity" : "swift-argument-parser",
14
+ "kind" : "remoteSourceControl",
15
+ "location" : "https://github.com/apple/swift-argument-parser",
16
+ "state" : {
17
+ "revision" : "df9ee6676cd5b3bf5b330ec7568a5644f547201b",
18
+ "version" : "1.1.3"
19
+ }
20
+ }
21
+ ],
22
+ "version" : 2
25
23
  }
package/Package.swift CHANGED
@@ -1,4 +1,4 @@
1
- // swift-tools-version:5.6
1
+ // swift-tools-version:5.7
2
2
  import PackageDescription
3
3
 
4
4
  let package = Package(
@@ -17,5 +17,7 @@ let package = Package(
17
17
  "Private/EmbeddedLibraries/README.md",
18
18
  "Private/EmbeddedLibraries/ZipFoundation/README.md",
19
19
  "Private/EmbeddedLibraries/EpoxyCore/README.md",
20
- ]),
20
+ "Private/EmbeddedLibraries/LRUCache/README.md",
21
+ ],
22
+ resources: [.copy("PrivacyInfo.xcprivacy")]),
21
23
  ])
package/README.md CHANGED
@@ -41,7 +41,7 @@ To install Lottie using [Swift Package Manager](https://github.com/apple/swift-p
41
41
  or you can add the following dependency to your `Package.swift`:
42
42
 
43
43
  ```swift
44
- .package(url: "https://github.com/airbnb/lottie-spm.git", from: "4.3.3")
44
+ .package(url: "https://github.com/airbnb/lottie-spm.git", from: "4.4.0")
45
45
  ```
46
46
 
47
47
  When using Swift Package Manager we recommend using the [lottie-spm](https://github.com/airbnb/lottie-spm) repo instead of the main lottie-ios repo. The main git repository for [lottie-ios](https://github.com/airbnb/lottie-ios) is somewhat large (300+ MB), and Swift Package Manager always downloads the full repository with all git history. The [lottie-spm](https://github.com/airbnb/lottie-spm) repo is much smaller (less than 500kb), so can be downloaded much more quickly.
@@ -75,9 +75,27 @@ carthage update
75
75
  ```
76
76
  In your application targets “General” tab under the “Linked Frameworks and Libraries” section, drag and drop lottie-ios.framework from the Carthage/Build/iOS directory that `carthage update` produced.
77
77
 
78
- ### Data collection
78
+ ## Swift Version Support
79
79
 
80
- The Lottie SDK does not collect any data. We provide this notice to help you fill out [App Privacy Details](https://developer.apple.com/app-store/app-privacy-details/).
80
+ Lottie supports Swift / Xcode versions back to the minimum version that is permited by Apple for submissions to the App Store. You can see the most up-to-date information for which Swift versions Lottie supports on [Swift Package Index](https://swiftpackageindex.com/airbnb/lottie-ios):
81
+
82
+ [![Swift Versions](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fairbnb%2Flottie-ios%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/airbnb/lottie-ios)
83
+
84
+ ## Privacy
85
+
86
+ Lottie does not collect any data. We provide this notice to help you fill out [App Privacy Details](https://developer.apple.com/app-store/app-privacy-details/). We additionally provide a [privacy manifest](https://github.com/airbnb/lottie-ios/blob/master/Sources/PrivacyInfo.xcprivacy) which can be included in your app.
87
+
88
+ ## Security
89
+
90
+ We distribute XCFramework bundles for each release on [GitHub](https://github.com/airbnb/lottie-ios/releases/latest). In Lottie 4.4.0 and later, these XCFramework bundles include a [code signature](https://developer.apple.com/documentation/xcode/verifying-the-origin-of-your-xcframeworks). These bundles are self-signed under the name "Lottie iOS" and have the following fingerprint:
91
+
92
+ ```
93
+ 89 2F 1B 43 04 7B 50 53 8F 2F 46 EA D9 29 00 DD 3D 48 11 F358 21 78 C0 61 A5 FB 20 F1 11 CB 26
94
+ ```
95
+
96
+ In Xcode you can verify this by selecting `Lottie.xcframework` and confirming that it shows the following information:
97
+
98
+ ![Code Signature in Xcode](_Gifs/code_signature.png)
81
99
 
82
100
  ## Contributing
83
101
 
package/Rakefile CHANGED
@@ -68,45 +68,60 @@ namespace :build do
68
68
  sh 'rm -rf .build/archives'
69
69
 
70
70
  # Build the framework for each supported platform, including simulators
71
- xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (iOS)" -destination generic/platform=iOS -archivePath ".build/archives/Lottie_iOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
72
- xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (iOS)" -destination "generic/platform=iOS Simulator" -archivePath ".build/archives/Lottie_iOS_Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
73
- xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (iOS)" -destination "generic/platform=macOS,variant=Mac Catalyst" -archivePath ".build/archives/Lottie_Mac_Catalyst" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
74
- xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (macOS)" -destination generic/platform=macOS -archivePath ".build/archives/Lottie_macOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
75
- xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (tvOS)" -destination generic/platform=tvOS -archivePath ".build/archives/Lottie_tvOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
76
- xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (tvOS)" -destination "generic/platform=tvOS Simulator" -archivePath ".build/archives/Lottie_tvOS_Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
71
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (iOS)" -destination generic/platform=iOS -archivePath ".build/archives/Lottie_iOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO SWIFT_SERIALIZE_DEBUGGING_OPTIONS=NO')
72
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (iOS)" -destination "generic/platform=iOS Simulator" -archivePath ".build/archives/Lottie_iOS_Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO SWIFT_SERIALIZE_DEBUGGING_OPTIONS=NO')
73
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (iOS)" -destination "generic/platform=macOS,variant=Mac Catalyst" -archivePath ".build/archives/Lottie_Mac_Catalyst" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO SWIFT_SERIALIZE_DEBUGGING_OPTIONS=NO')
74
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (macOS)" -destination generic/platform=macOS -archivePath ".build/archives/Lottie_macOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO SWIFT_SERIALIZE_DEBUGGING_OPTIONS=NO')
75
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (tvOS)" -destination generic/platform=tvOS -archivePath ".build/archives/Lottie_tvOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO SWIFT_SERIALIZE_DEBUGGING_OPTIONS=NO')
76
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (tvOS)" -destination "generic/platform=tvOS Simulator" -archivePath ".build/archives/Lottie_tvOS_Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO SWIFT_SERIALIZE_DEBUGGING_OPTIONS=NO')
77
77
 
78
78
  ifVisionOSEnabled {
79
- xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (visionOS)" -destination generic/platform=visionOS -archivePath ".build/archives/Lottie_visionOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
80
- xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (visionOS)" -destination "generic/platform=visionOS Simulator" -archivePath ".build/archives/Lottie_visionOS_Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
79
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (visionOS)" -destination generic/platform=visionOS -archivePath ".build/archives/Lottie_visionOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO SWIFT_SERIALIZE_DEBUGGING_OPTIONS=NO')
80
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (visionOS)" -destination "generic/platform=visionOS Simulator" -archivePath ".build/archives/Lottie_visionOS_Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO SWIFT_SERIALIZE_DEBUGGING_OPTIONS=NO')
81
81
  }
82
82
 
83
83
  # Combine all of the platforms into a single XCFramework
84
84
  xcframeworkInvocation = [
85
85
  '-create-xcframework',
86
- '-framework .build/archives/Lottie_iOS.xcarchive/Products/Library/Frameworks/Lottie.framework',
87
- '-framework .build/archives/Lottie_iOS_Simulator.xcarchive/Products/Library/Frameworks/Lottie.framework',
88
- '-framework .build/archives/Lottie_Mac_Catalyst.xcarchive/Products/Library/Frameworks/Lottie.framework',
89
- '-framework .build/archives/Lottie_tvOS.xcarchive/Products/Library/Frameworks/Lottie.framework',
90
- '-framework .build/archives/Lottie_tvOS_Simulator.xcarchive/Products/Library/Frameworks/Lottie.framework',
91
- '-framework .build/archives/Lottie_macOS.xcarchive/Products/Library/Frameworks/Lottie.framework',
86
+ '-archive .build/archives/Lottie_iOS.xcarchive -framework Lottie.framework',
87
+ '-archive .build/archives/Lottie_iOS_Simulator.xcarchive -framework Lottie.framework',
88
+ '-archive .build/archives/Lottie_Mac_Catalyst.xcarchive -framework Lottie.framework',
89
+ '-archive .build/archives/Lottie_tvOS.xcarchive -framework Lottie.framework',
90
+ '-archive .build/archives/Lottie_tvOS_Simulator.xcarchive -framework Lottie.framework',
91
+ '-archive .build/archives/Lottie_macOS.xcarchive -framework Lottie.framework',
92
92
  ]
93
93
 
94
94
  ifVisionOSEnabled {
95
- xcframeworkInvocation.push('-framework .build/archives/Lottie_visionOS.xcarchive/Products/Library/Frameworks/Lottie.framework')
96
- xcframeworkInvocation.push('-framework .build/archives/Lottie_visionOS_Simulator.xcarchive/Products/Library/Frameworks/Lottie.framework')
95
+ xcframeworkInvocation.push('-archive .build/archives/Lottie_visionOS.xcarchive -framework Lottie.framework')
96
+ xcframeworkInvocation.push('-archive .build/archives/Lottie_visionOS_Simulator.xcarchive -framework Lottie.framework')
97
97
  }
98
98
 
99
99
  xcframeworkInvocation.push('-output .build/archives/Lottie.xcframework')
100
100
 
101
101
  xcodebuild(xcframeworkInvocation.join(" "))
102
102
 
103
- # Archive the XCFramework into a zip file
104
103
  Dir.chdir('.build/archives') do
104
+ # Codesign the XCFramework using the "Lottie iOS" certificate, which should be installed in the keychain.
105
+ # - Check to make sure the certificate is installed before attemtping to codesign.
106
+ # - In GitHub actions CI, only jobs run by contibutors have access to repo secrets,
107
+ # so PR jobs from external contributors won't have access to this certificate.
108
+ # In that case we skip codesigning so the job doesn't fail.
109
+ puts "Checking if signing certificate is installed..."
110
+ `security find-certificate -c 'Lottie iOS'`
111
+ if $?.success?
112
+ puts "Signing certificate is installed. Code signing Lottie.xcframework."
113
+ sh 'codesign --timestamp -v --sign "Lottie iOS" Lottie.xcframework'
114
+ else
115
+ puts "Signing certificate is not installed. Lottie.xcframework will not be code signed."
116
+ end
117
+
118
+ # Archive the XCFramework into a zip file
105
119
  # Use --symlinks to avoid "Multiple binaries share the same codesign path. This can happen if your build process copies frameworks by following symlinks."
106
120
  # error when validating macOS apps (#1948)
107
121
  sh "zip -r --symlinks #{args[:zip_archive_name]}.xcframework.zip Lottie.xcframework"
108
122
  sh 'rm -rf Lottie.xcframework'
109
123
  end
124
+
110
125
  sh "swift package compute-checksum .build/archives/#{args[:zip_archive_name]}.xcframework.zip"
111
126
  end
112
127
  end
@@ -131,7 +146,9 @@ namespace :test do
131
146
  sh 'cp -R [^script]* script/test-carthage/Carthage/Checkouts/lottie-ios'
132
147
 
133
148
  Dir.chdir('script/test-carthage') do
134
- # Build the LottieCarthage framework scheme
149
+ installVisionOSIfNecessary()
150
+
151
+ # Build the Lottie framework scheme
135
152
  sh 'carthage build --use-xcframeworks'
136
153
 
137
154
  # Delete Carthage's derived data to verify that the built .xcframework doesn't depend on any
@@ -139,9 +156,11 @@ namespace :test do
139
156
  # https://github.com/airbnb/lottie-ios/issues/1492
140
157
  sh 'rm -rf ~/Library/Caches/org.carthage.CarthageKit/DerivedData'
141
158
 
142
- # Build a test app that imports and uses the LottieCarthage framework
143
- xcodebuild('build -scheme CarthageTest -destination "platform=iOS Simulator,name=iPhone SE (3rd generation)"')
144
- xcodebuild('build -scheme CarthageTest-macOS')
159
+ # Build a test app that imports and uses the Lottie framework built via Carthage
160
+ xcodebuild('build -scheme CarthageTest -destination "platform=iOS Simulator,name=iPhone SE (3rd generation)"')
161
+ xcodebuild('build -scheme CarthageTest -destination generic/platform=macOS')
162
+ xcodebuild('build -scheme CarthageTest -destination "platform=tvOS Simulator,name=Apple TV"')
163
+ xcodebuild('build -scheme CarthageTest -destination "platform=visionOS Simulator,name=Apple Vision Pro"')
145
164
  end
146
165
  end
147
166
 
@@ -232,16 +251,21 @@ def xcodebuild(command)
232
251
  end
233
252
 
234
253
  # Runs the given code block, unless `SKIP_VISION_OS=true`.
235
- # This can be removed once CI only uses Xcode 15+.
254
+ # TODO: Remove this once CI only uses Xcode 15.2+.
236
255
  def ifVisionOSEnabled
237
256
  if ENV["SKIP_VISION_OS"] == "true"
238
257
  puts "Skipping visionOS build"
239
258
  else
240
- # As of 9/5/23 the GitHub Actions runner doesn't include the visionOS SDK by default,
241
- # so we have to download it manually. Following the suggested workaround from
242
- # https://github.com/actions/runner-images/issues/8144#issuecomment-1702786388
243
- `brew install xcodesorg/made/xcodes`
244
- `xcodes runtimes install 'visionOS 1.0-beta3'`
259
+ installVisionOSIfNecessary()
245
260
  yield
246
261
  end
247
- end
262
+ end
263
+
264
+ def installVisionOSIfNecessary
265
+ # visionOS is unsupported by default on Intel, but we can override this
266
+ # https://github.com/actions/runner-images/issues/8144#issuecomment-1902072070
267
+ sh 'defaults write com.apple.dt.Xcode AllowUnsupportedVisionOSHost -bool YES'
268
+ sh 'defaults write com.apple.CoreSimulator AllowUnsupportedVisionOSHost -bool YES'
269
+
270
+ xcodebuild("-downloadPlatform visionOS")
271
+ end
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>NSPrivacyTracking</key>
6
+ <false/>
7
+ <key>NSPrivacyTrackingDomains</key>
8
+ <array/>
9
+ <key>NSPrivacyCollectedDataTypes</key>
10
+ <array/>
11
+ <key>NSPrivacyAccessedAPITypes</key>
12
+ <array>
13
+ <dict>
14
+ <key>NSPrivacyAccessedAPIType</key>
15
+ <string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
16
+ <key>NSPrivacyAccessedAPITypeReasons</key>
17
+ <array>
18
+ <string>3B52.1</string>
19
+ </array>
20
+ </dict>
21
+ </array>
22
+ </dict>
23
+ </plist>
@@ -21,7 +21,7 @@ extension CAAnimation {
21
21
  // 0% 100%
22
22
  //
23
23
  let baseAnimation = self
24
- baseAnimation.duration = context.animation.duration
24
+ baseAnimation.duration = context.animationDuration
25
25
  baseAnimation.speed = (context.endFrame < context.startFrame) ? -1 : 1
26
26
 
27
27
  // To select the subrange of the `baseAnimation` that should be played,
@@ -10,7 +10,7 @@ extension CALayer {
10
10
  /// Constructs a `CAKeyframeAnimation` that reflects the given keyframes,
11
11
  /// and adds it to this `CALayer`.
12
12
  @nonobjc
13
- func addAnimation<KeyframeValue, ValueRepresentation>(
13
+ func addAnimation<KeyframeValue: AnyInterpolatable, ValueRepresentation>(
14
14
  for property: LayerProperty<ValueRepresentation>,
15
15
  keyframes: KeyframeGroup<KeyframeValue>,
16
16
  value keyframeValueMapping: (KeyframeValue) throws -> ValueRepresentation,
@@ -39,7 +39,7 @@ extension CALayer {
39
39
  /// - If the value can be applied directly to the CALayer using KVC,
40
40
  /// then no `CAAnimation` will be created and the value will be applied directly.
41
41
  @nonobjc
42
- private func defaultAnimation<KeyframeValue, ValueRepresentation>(
42
+ private func defaultAnimation<KeyframeValue: AnyInterpolatable, ValueRepresentation>(
43
43
  for property: LayerProperty<ValueRepresentation>,
44
44
  keyframes keyframeGroup: KeyframeGroup<KeyframeValue>,
45
45
  value keyframeValueMapping: (KeyframeValue) throws -> ValueRepresentation,
@@ -61,9 +61,9 @@ extension CALayer {
61
61
  """)
62
62
  }
63
63
 
64
- // If there is exactly one keyframe value, we can improve performance
65
- // by applying that value directly to the layer instead of creating
66
- // a relatively expensive `CAKeyframeAnimation`.
64
+ // If there is exactly one keyframe value that doesn't animate,
65
+ // we can improve performance by applying that value directly to the layer
66
+ // instead of creating a relatively expensive `CAKeyframeAnimation`.
67
67
  if keyframes.count == 1 {
68
68
  return singleKeyframeAnimation(
69
69
  for: property,
@@ -71,6 +71,16 @@ extension CALayer {
71
71
  writeDirectlyToPropertyIfPossible: true)
72
72
  }
73
73
 
74
+ /// If we're required to use the `complexTimeRemapping` from some parent `PreCompLayer`,
75
+ /// we have to manually interpolate the keyframes with the time remapping applied.
76
+ if context.mustUseComplexTimeRemapping {
77
+ return try defaultAnimation(
78
+ for: property,
79
+ keyframes: Keyframes.manuallyInterpolatedWithTimeRemapping(keyframeGroup, context: context),
80
+ value: keyframeValueMapping,
81
+ context: context.withoutTimeRemapping())
82
+ }
83
+
74
84
  // Split the keyframes into segments with the same `CAAnimationCalculationMode` value
75
85
  // - Each of these segments will become their own `CAKeyframeAnimation`
76
86
  let animationSegments = keyframes.segmentsSplitByCalculationMode()
@@ -179,8 +189,8 @@ extension CALayer {
179
189
  // all of which have a non-zero number of keyframes.
180
190
  let segmentAnimations: [CAKeyframeAnimation] = try animationSegments.indices.map { index in
181
191
  let animationSegment = animationSegments[index]
182
- var segmentStartTime = context.time(for: animationSegment.first!.time)
183
- var segmentEndTime = context.time(for: animationSegment.last!.time)
192
+ var segmentStartTime = try context.time(forFrame: animationSegment.first!.time)
193
+ var segmentEndTime = try context.time(forFrame: animationSegment.last!.time)
184
194
 
185
195
  // Every portion of the animation timeline has to be covered by a `CAKeyframeAnimation`,
186
196
  // so if this is the first or last segment then the start/end time should be exactly
@@ -190,13 +200,13 @@ extension CALayer {
190
200
 
191
201
  if isFirstSegment {
192
202
  segmentStartTime = min(
193
- context.time(for: context.animation.startFrame),
203
+ try context.time(forFrame: context.animation.startFrame),
194
204
  segmentStartTime)
195
205
  }
196
206
 
197
207
  if isLastSegment {
198
208
  segmentEndTime = max(
199
- context.time(for: context.animation.endFrame),
209
+ try context.time(forFrame: context.animation.endFrame),
200
210
  segmentEndTime)
201
211
  }
202
212
 
@@ -206,8 +216,8 @@ extension CALayer {
206
216
  // relative to 0 (`segmentStartTime`) and 1 (`segmentEndTime`). This is different
207
217
  // from the default behavior of the `keyframeAnimation` method, where times
208
218
  // are expressed relative to the entire animation duration.
209
- let customKeyTimes = animationSegment.map { keyframeModel -> NSNumber in
210
- let keyframeTime = context.time(for: keyframeModel.time)
219
+ let customKeyTimes = try animationSegment.map { keyframeModel -> NSNumber in
220
+ let keyframeTime = try context.time(forFrame: keyframeModel.time)
211
221
  let segmentProgressTime = ((keyframeTime - segmentStartTime) / segmentDuration)
212
222
  return segmentProgressTime as NSNumber
213
223
  }
@@ -241,8 +251,8 @@ extension CALayer {
241
251
  {
242
252
  // Convert the list of `Keyframe<T>` into
243
253
  // the representation used by `CAKeyframeAnimation`
244
- var keyTimes = customKeyTimes ?? keyframes.map { keyframeModel -> NSNumber in
245
- NSNumber(value: Float(context.progressTime(for: keyframeModel.time)))
254
+ var keyTimes = try customKeyTimes ?? keyframes.map { keyframeModel -> NSNumber in
255
+ NSNumber(value: Float(try context.progressTime(for: keyframeModel.time)))
246
256
  }
247
257
 
248
258
  var timingFunctions = timingFunctions(for: keyframes)
@@ -53,7 +53,7 @@ extension CGPath {
53
53
  // MARK: - BezierPathKeyframe
54
54
 
55
55
  /// Data that represents how to render a bezier path at a specific point in time
56
- struct BezierPathKeyframe {
56
+ struct BezierPathKeyframe: Interpolatable {
57
57
  let path: BezierPath
58
58
  let cornerRadius: LottieVector1D?
59
59
 
@@ -65,7 +65,7 @@ struct BezierPathKeyframe {
65
65
  -> KeyframeGroup<BezierPathKeyframe>
66
66
  {
67
67
  guard
68
- let cornerRadius = cornerRadius,
68
+ let cornerRadius,
69
69
  cornerRadius.keyframes.contains(where: { $0.value.cgFloatValue > 0 })
70
70
  else {
71
71
  return path.map { path in
@@ -77,4 +77,10 @@ struct BezierPathKeyframe {
77
77
  path, cornerRadius,
78
78
  makeCombinedResult: BezierPathKeyframe.init)
79
79
  }
80
+
81
+ func interpolate(to: BezierPathKeyframe, amount: CGFloat) -> BezierPathKeyframe {
82
+ BezierPathKeyframe(
83
+ path: path.interpolate(to: to.path, amount: amount),
84
+ cornerRadius: cornerRadius.interpolate(to: to.cornerRadius, amount: amount))
85
+ }
80
86
  }
@@ -29,9 +29,15 @@ extension CAShapeLayer {
29
29
 
30
30
  extension Ellipse {
31
31
  /// Data that represents how to render an ellipse at a specific point in time
32
- struct Keyframe {
32
+ struct Keyframe: Interpolatable {
33
33
  let size: LottieVector3D
34
34
  let position: LottieVector3D
35
+
36
+ func interpolate(to: Ellipse.Keyframe, amount: CGFloat) -> Ellipse.Keyframe {
37
+ Keyframe(
38
+ size: size.interpolate(to: to.size, amount: amount),
39
+ position: position.interpolate(to: to.position, amount: amount))
40
+ }
35
41
  }
36
42
 
37
43
  /// Creates a single array of animatable keyframes from the separate arrays of keyframes in this Ellipse
@@ -114,7 +114,7 @@ extension GradientRenderLayer {
114
114
 
115
115
  let combinedKeyframes = Keyframes.combined(
116
116
  gradient.startPoint, gradient.endPoint,
117
- makeCombinedResult: { absoluteStartPoint, absoluteEndPoint -> (startPoint: CGPoint, endPoint: CGPoint) in
117
+ makeCombinedResult: { absoluteStartPoint, absoluteEndPoint -> RadialGradientKeyframes in
118
118
  // Convert the absolute start / end points to the relative structure used by Core Animation
119
119
  let relativeStartPoint = percentBasedPointInBounds(from: absoluteStartPoint.pointValue)
120
120
  let radius = absoluteStartPoint.pointValue.distanceTo(absoluteEndPoint.pointValue)
@@ -123,7 +123,7 @@ extension GradientRenderLayer {
123
123
  x: absoluteStartPoint.x + radius,
124
124
  y: absoluteStartPoint.y + radius))
125
125
 
126
- return (startPoint: relativeStartPoint, endPoint: relativeEndPoint)
126
+ return RadialGradientKeyframes(startPoint: relativeStartPoint, endPoint: relativeEndPoint)
127
127
  })
128
128
 
129
129
  try addAnimation(
@@ -140,6 +140,19 @@ extension GradientRenderLayer {
140
140
  }
141
141
  }
142
142
 
143
+ // MARK: - RadialGradientKeyframes
144
+
145
+ private struct RadialGradientKeyframes: Interpolatable {
146
+ let startPoint: CGPoint
147
+ let endPoint: CGPoint
148
+
149
+ func interpolate(to: RadialGradientKeyframes, amount: CGFloat) -> RadialGradientKeyframes {
150
+ RadialGradientKeyframes(
151
+ startPoint: startPoint.interpolate(to: to.startPoint, amount: amount),
152
+ endPoint: endPoint.interpolate(to: to.endPoint, amount: amount))
153
+ }
154
+ }
155
+
143
156
  // MARK: - GradientContentType
144
157
 
145
158
  /// Each type of gradient that can be constructed from a `GradientShapeItem`
@@ -165,11 +165,18 @@ extension LayerProperty {
165
165
  customizableProperty: .opacity)
166
166
  }
167
167
 
168
+ static var isHidden: LayerProperty<Bool> {
169
+ .init(
170
+ caLayerKeypath: #keyPath(CALayer.isHidden),
171
+ defaultValue: false,
172
+ customizableProperty: nil /* unsupported */ )
173
+ }
174
+
168
175
  static var transform: LayerProperty<CATransform3D> {
169
176
  .init(
170
177
  caLayerKeypath: #keyPath(CALayer.transform),
171
178
  isDefaultValue: { transform in
172
- guard let transform = transform else { return false }
179
+ guard let transform else { return false }
173
180
  return CATransform3DIsIdentity(transform)
174
181
  },
175
182
  customizableProperty: nil /* currently unsupported */ )
@@ -31,10 +31,17 @@ extension CAShapeLayer {
31
31
 
32
32
  extension Rectangle {
33
33
  /// Data that represents how to render a rectangle at a specific point in time
34
- struct Keyframe {
34
+ struct Keyframe: Interpolatable {
35
35
  let size: LottieVector3D
36
36
  let position: LottieVector3D
37
37
  let cornerRadius: LottieVector1D
38
+
39
+ func interpolate(to: Rectangle.Keyframe, amount: CGFloat) -> Rectangle.Keyframe {
40
+ Rectangle.Keyframe(
41
+ size: size.interpolate(to: to.size, amount: amount),
42
+ position: position.interpolate(to: to.position, amount: amount),
43
+ cornerRadius: cornerRadius.interpolate(to: to.cornerRadius, amount: amount))
44
+ }
38
45
  }
39
46
 
40
47
  /// Creates a single array of animatable keyframes from the separate arrays of keyframes in this Rectangle
@@ -80,7 +80,7 @@ extension CAShapeLayer {
80
80
 
81
81
  extension Star {
82
82
  /// Data that represents how to render a star at a specific point in time
83
- struct Keyframe {
83
+ struct Keyframe: Interpolatable {
84
84
  let position: LottieVector3D
85
85
  let outerRadius: LottieVector1D
86
86
  let innerRadius: LottieVector1D
@@ -88,6 +88,17 @@ extension Star {
88
88
  let innerRoundness: LottieVector1D
89
89
  let points: LottieVector1D
90
90
  let rotation: LottieVector1D
91
+
92
+ func interpolate(to: Star.Keyframe, amount: CGFloat) -> Star.Keyframe {
93
+ Star.Keyframe(
94
+ position: position.interpolate(to: to.position, amount: amount),
95
+ outerRadius: outerRadius.interpolate(to: to.outerRadius, amount: amount),
96
+ innerRadius: innerRadius.interpolate(to: to.innerRadius, amount: amount),
97
+ outerRoundness: outerRoundness.interpolate(to: to.outerRoundness, amount: amount),
98
+ innerRoundness: innerRoundness.interpolate(to: to.innerRoundness, amount: amount),
99
+ points: points.interpolate(to: to.points, amount: amount),
100
+ rotation: rotation.interpolate(to: to.rotation, amount: amount))
101
+ }
91
102
  }
92
103
 
93
104
  /// Creates a single array of animatable keyframes from the separate arrays of keyframes in this star/polygon
@@ -1,7 +1,6 @@
1
1
  // Created by Cal Stephens on 2/10/22.
2
2
  // Copyright © 2022 Airbnb Inc. All rights reserved.
3
3
 
4
- import Foundation
5
4
  import QuartzCore
6
5
 
7
6
  // MARK: - StrokeShapeItem
@@ -253,10 +253,16 @@ extension CALayer {
253
253
  context: LayerAnimationContext)
254
254
  throws
255
255
  {
256
- // Core Animation doesn't animate skew changes properly. If the skew value
257
- // changes over the course of the animation then we have to manually
258
- // compute the `CATransform3D` for each frame individually.
259
- let requiresManualInterpolation = transformModel.hasSkewAnimation
256
+ let requiresManualInterpolation =
257
+ // Core Animation doesn't animate skew changes properly. If the skew value
258
+ // changes over the course of the animation then we have to manually
259
+ // compute the `CATransform3D` for each frame individually.
260
+ transformModel.hasSkewAnimation
261
+ // `addAnimation` requires that we use an `Interpolatable` value, but we can't interpolate a `CATransform3D`.
262
+ // Since this is only necessary when using `complexTimeRemapping`, we can avoid this by manually interpolating
263
+ // when `context.mustUseComplexTimeRemapping` is true and just returning a `Hold` container.
264
+ // Since our keyframes are already manually interpolated, they won't need to be interpolated again.
265
+ || context.mustUseComplexTimeRemapping
260
266
 
261
267
  let combinedTransformKeyframes = Keyframes.combined(
262
268
  transformModel.anchorPoint,
@@ -272,7 +278,7 @@ extension CALayer {
272
278
  requiresManualInterpolation: requiresManualInterpolation,
273
279
  makeCombinedResult: {
274
280
  anchor, position, positionX, positionY, scale, rotationX, rotationY, rotationZ, skew, skewAxis
275
- -> CATransform3D in
281
+ -> Hold<CATransform3D> in
276
282
 
277
283
  let transformPosition: CGPoint
278
284
  if transformModel._positionX != nil, transformModel._positionY != nil {
@@ -281,7 +287,7 @@ extension CALayer {
281
287
  transformPosition = position.pointValue
282
288
  }
283
289
 
284
- return CATransform3D.makeTransform(
290
+ let transform = CATransform3D.makeTransform(
285
291
  anchor: anchor.pointValue,
286
292
  position: transformPosition,
287
293
  scale: scale.sizeValue,
@@ -290,12 +296,14 @@ extension CALayer {
290
296
  rotationZ: rotationZ.cgFloatValue,
291
297
  skew: skew.cgFloatValue,
292
298
  skewAxis: skewAxis.cgFloatValue)
299
+
300
+ return Hold(value: transform)
293
301
  })
294
302
 
295
303
  try addAnimation(
296
304
  for: .transform,
297
305
  keyframes: combinedTransformKeyframes,
298
- value: { $0 },
306
+ value: { $0.value },
299
307
  context: context)
300
308
  }
301
309
 
@@ -305,8 +313,8 @@ extension TransformModel {
305
313
  /// Whether or not this transform has a non-zero skew value
306
314
  var hasSkew: Bool {
307
315
  guard
308
- let _skew = _skew,
309
- let _skewAxis = _skewAxis,
316
+ let _skew,
317
+ let _skewAxis,
310
318
  !_skew.keyframes.isEmpty,
311
319
  !_skewAxis.keyframes.isEmpty
312
320
  else {
@@ -320,8 +328,8 @@ extension TransformModel {
320
328
  var hasSkewAnimation: Bool {
321
329
  guard
322
330
  hasSkew,
323
- let _skew = _skew,
324
- let _skewAxis = _skewAxis
331
+ let _skew,
332
+ let _skewAxis
325
333
  else { return false }
326
334
 
327
335
  return _skew.keyframes.count > 1