lottie-ios 3.4.2 → 3.4.4

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 (175) hide show
  1. package/.github/FUNDING.yml +0 -11
  2. package/Lottie.xcodeproj/project.pbxproj +6 -2
  3. package/Lottie.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +22 -0
  4. package/Lottie.xcworkspace/xcshareddata/swiftpm/Package.resolved +2 -2
  5. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  6. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +16 -0
  7. package/Sources/Private/CoreAnimation/Animations/CombinedShapeAnimation.swift +10 -7
  8. package/Sources/Private/CoreAnimation/Animations/GradientAnimations.swift +17 -5
  9. package/Sources/Private/CoreAnimation/Animations/StrokeAnimation.swift +5 -4
  10. package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +25 -12
  11. package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +3 -0
  12. package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +1 -0
  13. package/Sources/Private/CoreAnimation/Layers/PreCompLayer.swift +2 -2
  14. package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +4 -4
  15. package/Sources/Private/CoreAnimation/Layers/TextLayer.swift +15 -1
  16. package/Sources/Private/MainThread/NodeRenderSystem/Extensions/ItemsExtension.swift +1 -1
  17. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +1 -1
  18. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +12 -1
  19. package/Sources/Private/Model/Objects/Marker.swift +4 -0
  20. package/Sources/Public/Animation/AnimationPublic.swift +17 -1
  21. package/Sources/Public/Animation/AnimationView.swift +43 -0
  22. package/Sources/Public/iOS/AnimatedButton.swift +25 -23
  23. package/Sources/Public/iOS/AnimatedSwitch.swift +15 -10
  24. package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +15 -0
  25. package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~4VqMqsI5lOfxRppnud6-VDWcNsU8J7VgFCJfW2dXPwOcAkvU-I8Um5yp9n0Zv6nr3VmcxYggaVMDFfR0U_vjKw== +1 -0
  26. package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~7IO4FqH8-VUsSfuA0_wMGUgfGdV7MwzpkzjwWbiB50jXZKQRHtU4G0A7ZXTvsR4mDdrWiawpNC_eriOALfZ7Og== +0 -0
  27. package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~ClRW5-6rvrgZHHbrPS1VEREaxelpVcKlERPzSa3f2rtFNXdoqATxFCsPge3a_nO4mhhtpmTmbYvyfI-obAu8Qg== +0 -0
  28. package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~Jr2dFGcJc1188nMoMha82z9wM99lCLm4MKzhjfVVjIxKeCZVGWGZH3HgJFNkTpozdk5p1u5o91dCiY4-cR1Zhg== +0 -0
  29. package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~MBDeCFoahVzzmtSQrVhhy9VMJV6CmcnUkFya_qwe0XsMFscmY2nS8dxrTEYkY16sH68sEH7WVCCzTuW1z1Hocw== +1 -0
  30. package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~U4plJajEDM_uSZdglhDyj6gQO63mbeKxYP9X94aIaOtL0yErtfQnGz5SgsqQ0ToSZcJj6Ao9Wx-nesZVwBTRuw== +0 -0
  31. package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~UsLY5O-vDMEHEe3bF8XJvkKkYxdsKlIytgJW7zgNuetw53fG3SssfZiiMfbuYnOtvvvYBXTwpbz07V6Czp029Q== +0 -0
  32. package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~cpg4cb7H0TaehHy4a-xINJMFfBM2_AEHal_0tIX8ymNVJPsjlCysi3-Cad0Ca_SvuGwVM7ONF55OBUuC9YKZvA== +0 -0
  33. package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~cuHjdTCuciVumvEpvozxwDj2wAdgWe13bzd1pUAGN45-WOgY0kIid9aUlBX675OnS-xNEc_pyQWo0RO1dKAjcw== +0 -0
  34. package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~f_-bfn-KRHHFg39_MtwgBulEAuWH6F05yqGYydXhil6kqZ51eAoRX6tsiqOr0Oa6eL3dK1tcCBD1bWX5orCZhw== +1 -0
  35. package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~jdIx7vWS6ej1cqYcbCS4KyZErSMF13ELR95YwClVc98tIXcsglh7KuGvI1gIxEPDtPXQpfC3XijIAGn1quL8Dw== +0 -0
  36. package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~lz3e0YeBa8TvMBSSymToh--gc6zznUIdH2jO0AJ4so5OPNdw6wpYmJebhaENGsRoD3beUXvlyD5f7_WeZrzyNQ== +0 -0
  37. package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~u_5dWbliYJ__YkyUCDFjdxiFds9M8Epr_RvbU1rIRCbBQdhRJ_TUBXXcL_Qq-wVp4umNLTOzud4OpQItSpO6Rg== +0 -0
  38. package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~v-YGzhC2l_icsAsyp1XAbEWYwxNook-rARAWlVZINioEsgT9LGvhg2oh78nYqoeH78m1fihr5HCcGNfF7SQj0g== +0 -0
  39. package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~4VqMqsI5lOfxRppnud6-VDWcNsU8J7VgFCJfW2dXPwOcAkvU-I8Um5yp9n0Zv6nr3VmcxYggaVMDFfR0U_vjKw== +0 -0
  40. package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~7IO4FqH8-VUsSfuA0_wMGUgfGdV7MwzpkzjwWbiB50jXZKQRHtU4G0A7ZXTvsR4mDdrWiawpNC_eriOALfZ7Og== +0 -0
  41. package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~ClRW5-6rvrgZHHbrPS1VEREaxelpVcKlERPzSa3f2rtFNXdoqATxFCsPge3a_nO4mhhtpmTmbYvyfI-obAu8Qg== +0 -0
  42. package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~Jr2dFGcJc1188nMoMha82z9wM99lCLm4MKzhjfVVjIxKeCZVGWGZH3HgJFNkTpozdk5p1u5o91dCiY4-cR1Zhg== +0 -0
  43. package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~MBDeCFoahVzzmtSQrVhhy9VMJV6CmcnUkFya_qwe0XsMFscmY2nS8dxrTEYkY16sH68sEH7WVCCzTuW1z1Hocw== +0 -0
  44. package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~U4plJajEDM_uSZdglhDyj6gQO63mbeKxYP9X94aIaOtL0yErtfQnGz5SgsqQ0ToSZcJj6Ao9Wx-nesZVwBTRuw== +0 -0
  45. package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~UsLY5O-vDMEHEe3bF8XJvkKkYxdsKlIytgJW7zgNuetw53fG3SssfZiiMfbuYnOtvvvYBXTwpbz07V6Czp029Q== +0 -0
  46. package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~cpg4cb7H0TaehHy4a-xINJMFfBM2_AEHal_0tIX8ymNVJPsjlCysi3-Cad0Ca_SvuGwVM7ONF55OBUuC9YKZvA== +0 -0
  47. package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~cuHjdTCuciVumvEpvozxwDj2wAdgWe13bzd1pUAGN45-WOgY0kIid9aUlBX675OnS-xNEc_pyQWo0RO1dKAjcw== +0 -0
  48. package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~f_-bfn-KRHHFg39_MtwgBulEAuWH6F05yqGYydXhil6kqZ51eAoRX6tsiqOr0Oa6eL3dK1tcCBD1bWX5orCZhw== +0 -0
  49. package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~jdIx7vWS6ej1cqYcbCS4KyZErSMF13ELR95YwClVc98tIXcsglh7KuGvI1gIxEPDtPXQpfC3XijIAGn1quL8Dw== +0 -0
  50. package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~lz3e0YeBa8TvMBSSymToh--gc6zznUIdH2jO0AJ4so5OPNdw6wpYmJebhaENGsRoD3beUXvlyD5f7_WeZrzyNQ== +0 -0
  51. package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~u_5dWbliYJ__YkyUCDFjdxiFds9M8Epr_RvbU1rIRCbBQdhRJ_TUBXXcL_Qq-wVp4umNLTOzud4OpQItSpO6Rg== +0 -0
  52. package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~v-YGzhC2l_icsAsyp1XAbEWYwxNook-rARAWlVZINioEsgT9LGvhg2oh78nYqoeH78m1fihr5HCcGNfF7SQj0g== +0 -0
  53. package/Tests/Artifacts/LottieTests.xcresult/Info.plist +29 -0
  54. package/Tests/AutomaticEngineTests.swift +1 -0
  55. package/Tests/SnapshotConfiguration.swift +15 -1
  56. package/Tests/SnapshotTests.swift +4 -0
  57. package/Tests/Utils/HardcodedTextProvider.swift +27 -0
  58. package/lottie-ios.podspec +1 -1
  59. package/package.json +1 -1
  60. package/Tests/Samples/9squares_AlBoardman.json +0 -1
  61. package/Tests/Samples/Boat_Loader.json +0 -1
  62. package/Tests/Samples/HamburgerArrow.json +0 -1
  63. package/Tests/Samples/IconTransitions.json +0 -1
  64. package/Tests/Samples/Images/dog.png +0 -0
  65. package/Tests/Samples/Issues/issue_1125.json +0 -1
  66. package/Tests/Samples/Issues/issue_1260.json +0 -1
  67. package/Tests/Samples/Issues/issue_1403.json +0 -1
  68. package/Tests/Samples/Issues/issue_1407.json +0 -1
  69. package/Tests/Samples/Issues/issue_1460.json +0 -1
  70. package/Tests/Samples/Issues/issue_1488.json +0 -1
  71. package/Tests/Samples/Issues/issue_1505.json +0 -1
  72. package/Tests/Samples/Issues/issue_1541.json +0 -1
  73. package/Tests/Samples/Issues/issue_1557.json +0 -1
  74. package/Tests/Samples/Issues/issue_1603.json +0 -1
  75. package/Tests/Samples/Issues/issue_1628.json +0 -1
  76. package/Tests/Samples/Issues/issue_1636.json +0 -1
  77. package/Tests/Samples/Issues/issue_1643.json +0 -1
  78. package/Tests/Samples/Issues/issue_1655.json +0 -1
  79. package/Tests/Samples/Issues/issue_1664.json +0 -1
  80. package/Tests/Samples/Issues/issue_1683.json +0 -1
  81. package/Tests/Samples/Issues/issue_1687.json +0 -1
  82. package/Tests/Samples/Issues/issue_1711.json +0 -1
  83. package/Tests/Samples/Issues/issue_1717.json +0 -1
  84. package/Tests/Samples/Issues/issue_769.json +0 -1
  85. package/Tests/Samples/Issues/issue_885.json +0 -1
  86. package/Tests/Samples/Issues/issue_965.json +0 -1
  87. package/Tests/Samples/Issues/pr_1536.json +0 -1
  88. package/Tests/Samples/Issues/pr_1563.json +0 -8439
  89. package/Tests/Samples/Issues/pr_1592.json +0 -5527
  90. package/Tests/Samples/Issues/pr_1599.json +0 -738
  91. package/Tests/Samples/Issues/pr_1604_1.json +0 -1
  92. package/Tests/Samples/Issues/pr_1604_2.json +0 -1
  93. package/Tests/Samples/Issues/pr_1632_1.json +0 -1
  94. package/Tests/Samples/Issues/pr_1632_2.json +0 -1
  95. package/Tests/Samples/Issues/pr_1686.json +0 -513
  96. package/Tests/Samples/Issues/pr_1698.json +0 -1
  97. package/Tests/Samples/Issues/pr_1699.json +0 -1
  98. package/Tests/Samples/LottieFiles/LICENSE.md +0 -14
  99. package/Tests/Samples/LottieFiles/bounce_strokes.json +0 -1
  100. package/Tests/Samples/LottieFiles/cactus.json +0 -1
  101. package/Tests/Samples/LottieFiles/dog_car_ride.json +0 -1
  102. package/Tests/Samples/LottieFiles/draft_icon.json +0 -1
  103. package/Tests/Samples/LottieFiles/fireworks.json +0 -1
  104. package/Tests/Samples/LottieFiles/gradient_1.json +0 -1
  105. package/Tests/Samples/LottieFiles/gradient_2.json +0 -1
  106. package/Tests/Samples/LottieFiles/gradient_pill.json +0 -1
  107. package/Tests/Samples/LottieFiles/gradient_shapes.json +0 -1
  108. package/Tests/Samples/LottieFiles/gradient_square.json +0 -1
  109. package/Tests/Samples/LottieFiles/growth.json +0 -1
  110. package/Tests/Samples/LottieFiles/infinity_loader.json +0 -1
  111. package/Tests/Samples/LottieFiles/loading_dots_1.json +0 -1
  112. package/Tests/Samples/LottieFiles/loading_dots_2.json +0 -1
  113. package/Tests/Samples/LottieFiles/loading_dots_3.json +0 -1
  114. package/Tests/Samples/LottieFiles/loading_gradient_strokes.json +0 -1
  115. package/Tests/Samples/LottieFiles/settings_slider.json +0 -1
  116. package/Tests/Samples/LottieFiles/shop.json +0 -1
  117. package/Tests/Samples/LottieFiles/step_loader.json +0 -1
  118. package/Tests/Samples/LottieLogo1.json +0 -1
  119. package/Tests/Samples/LottieLogo1_masked.json +0 -1
  120. package/Tests/Samples/LottieLogo2.json +0 -1
  121. package/Tests/Samples/MotionCorpse_Jrcanest.json +0 -1
  122. package/Tests/Samples/Nonanimating/BasicLayers.json +0 -1
  123. package/Tests/Samples/Nonanimating/DisableNodesTest.json +0 -1
  124. package/Tests/Samples/Nonanimating/FirstText.json +0 -1
  125. package/Tests/Samples/Nonanimating/GeometryTransformTest.json +0 -1
  126. package/Tests/Samples/Nonanimating/Text_AnimatedProperties.json +0 -1
  127. package/Tests/Samples/Nonanimating/Text_Glyph.json +0 -1
  128. package/Tests/Samples/Nonanimating/Text_NoAnimation.json +0 -1
  129. package/Tests/Samples/Nonanimating/Text_NoGlyph.json +0 -1
  130. package/Tests/Samples/Nonanimating/Zoom.json +0 -1
  131. package/Tests/Samples/Nonanimating/_dog.json +0 -1
  132. package/Tests/Samples/Nonanimating/base64Test.json +0 -1
  133. package/Tests/Samples/Nonanimating/blend_mode_test.json +0 -1
  134. package/Tests/Samples/Nonanimating/keypathTest.json +0 -1
  135. package/Tests/Samples/Nonanimating/verifyLineHeight.json +0 -1
  136. package/Tests/Samples/PinJump.json +0 -1
  137. package/Tests/Samples/Switch.json +0 -1
  138. package/Tests/Samples/Switch_States.json +0 -1
  139. package/Tests/Samples/TwitterHeart.json +0 -1
  140. package/Tests/Samples/TwitterHeartButton.json +0 -1
  141. package/Tests/Samples/TypeFace/A.json +0 -1
  142. package/Tests/Samples/TypeFace/Apostrophe.json +0 -1
  143. package/Tests/Samples/TypeFace/B.json +0 -1
  144. package/Tests/Samples/TypeFace/BlinkingCursor.json +0 -1
  145. package/Tests/Samples/TypeFace/C.json +0 -1
  146. package/Tests/Samples/TypeFace/Colon.json +0 -1
  147. package/Tests/Samples/TypeFace/Comma.json +0 -1
  148. package/Tests/Samples/TypeFace/D.json +0 -1
  149. package/Tests/Samples/TypeFace/E.json +0 -1
  150. package/Tests/Samples/TypeFace/F.json +0 -1
  151. package/Tests/Samples/TypeFace/G.json +0 -1
  152. package/Tests/Samples/TypeFace/H.json +0 -1
  153. package/Tests/Samples/TypeFace/I.json +0 -1
  154. package/Tests/Samples/TypeFace/J.json +0 -1
  155. package/Tests/Samples/TypeFace/K.json +0 -1
  156. package/Tests/Samples/TypeFace/L.json +0 -1
  157. package/Tests/Samples/TypeFace/M.json +0 -1
  158. package/Tests/Samples/TypeFace/N.json +0 -1
  159. package/Tests/Samples/TypeFace/O.json +0 -1
  160. package/Tests/Samples/TypeFace/P.json +0 -1
  161. package/Tests/Samples/TypeFace/Q.json +0 -1
  162. package/Tests/Samples/TypeFace/R.json +0 -1
  163. package/Tests/Samples/TypeFace/S.json +0 -1
  164. package/Tests/Samples/TypeFace/T.json +0 -1
  165. package/Tests/Samples/TypeFace/U.json +0 -1
  166. package/Tests/Samples/TypeFace/V.json +0 -1
  167. package/Tests/Samples/TypeFace/W.json +0 -1
  168. package/Tests/Samples/TypeFace/X.json +0 -1
  169. package/Tests/Samples/TypeFace/Y.json +0 -1
  170. package/Tests/Samples/TypeFace/Z.json +0 -1
  171. package/Tests/Samples/Watermelon.json +0 -1
  172. package/Tests/Samples/setValueTest.json +0 -1
  173. package/Tests/Samples/timeremap.json +0 -1
  174. package/Tests/Samples/vcTransition1.json +0 -1
  175. package/Tests/Samples/vcTransition2.json +0 -1
@@ -1,12 +1 @@
1
- # These are supported funding model platforms
2
-
3
- github: [buba447]
4
- patreon: # Replace with a single Patreon username
5
1
  open_collective: lottie
6
- ko_fi: # Replace with a single Ko-fi username
7
- tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8
- community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9
- liberapay: # Replace with a single Liberapay username
10
- issuehunt: # Replace with a single IssueHunt username
11
- otechie: # Replace with a single Otechie username
12
- custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
@@ -554,6 +554,7 @@
554
554
  2EAF5B0427A0798700E00531 /* AnimationFontProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EAF59F227A0798700E00531 /* AnimationFontProvider.swift */; };
555
555
  2EAF5B0527A0798700E00531 /* AnimationFontProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EAF59F227A0798700E00531 /* AnimationFontProvider.swift */; };
556
556
  2EAF5B0627A0798700E00531 /* AnimationFontProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EAF59F227A0798700E00531 /* AnimationFontProvider.swift */; };
557
+ 36E57EAC28AF7ADF00B7EFDA /* HardcodedTextProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E57EAB28AF7ADF00B7EFDA /* HardcodedTextProvider.swift */; };
557
558
  6D0E635F28246BD0007C5DB6 /* Difference in Frameworks */ = {isa = PBXBuildFile; productRef = 6D0E635E28246BD0007C5DB6 /* Difference */; };
558
559
  6D99D6432823790700E5205B /* LegacyGradientFillRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D99D6422823790700E5205B /* LegacyGradientFillRenderer.swift */; };
559
560
  6D99D6442823790700E5205B /* LegacyGradientFillRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D99D6422823790700E5205B /* LegacyGradientFillRenderer.swift */; };
@@ -783,6 +784,7 @@
783
784
  2EAF59EF27A0798700E00531 /* GradientValueProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientValueProvider.swift; sourceTree = "<group>"; };
784
785
  2EAF59F027A0798700E00531 /* PointValueProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PointValueProvider.swift; sourceTree = "<group>"; };
785
786
  2EAF59F227A0798700E00531 /* AnimationFontProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimationFontProvider.swift; sourceTree = "<group>"; };
787
+ 36E57EAB28AF7ADF00B7EFDA /* HardcodedTextProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HardcodedTextProvider.swift; sourceTree = "<group>"; };
786
788
  6D99D6422823790700E5205B /* LegacyGradientFillRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyGradientFillRenderer.swift; sourceTree = "<group>"; };
787
789
  6DB3BDB528243FA5002A276D /* ValueProvidersTests.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = ValueProvidersTests.swift; sourceTree = "<group>"; tabWidth = 2; };
788
790
  6DB3BDB7282454A6002A276D /* DictionaryInitializable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DictionaryInitializable.swift; sourceTree = "<group>"; };
@@ -876,6 +878,7 @@
876
878
  2E8040BF27A07343006E74CB /* Snapshotting+presentationLayer.swift */,
877
879
  2EAF59A627A076BC00E00531 /* Bundle+Module.swift */,
878
880
  2E09FA0527B6CEB600BA84E5 /* HardcodedFontProvider.swift */,
881
+ 36E57EAB28AF7ADF00B7EFDA /* HardcodedTextProvider.swift */,
879
882
  );
880
883
  path = Utils;
881
884
  sourceTree = "<group>";
@@ -1876,6 +1879,7 @@
1876
1879
  6DEF696E2824A76C007D640F /* BundleTests.swift in Sources */,
1877
1880
  2EAF59A727A076BC00E00531 /* Bundle+Module.swift in Sources */,
1878
1881
  2E8044AE27A07347006E74CB /* Snapshotting+presentationLayer.swift in Sources */,
1882
+ 36E57EAC28AF7ADF00B7EFDA /* HardcodedTextProvider.swift in Sources */,
1879
1883
  2E72128527BB32DB0027BC56 /* PerformanceTests.swift in Sources */,
1880
1884
  6DB3BDC328245AA2002A276D /* ParsingTests.swift in Sources */,
1881
1885
  6DB3BDB628243FA5002A276D /* ValueProvidersTests.swift in Sources */,
@@ -2670,8 +2674,8 @@
2670
2674
  isa = XCRemoteSwiftPackageReference;
2671
2675
  repositoryURL = "https://github.com/pointfreeco/swift-snapshot-testing.git";
2672
2676
  requirement = {
2673
- branch = main;
2674
- kind = branch;
2677
+ kind = revision;
2678
+ revision = 0c2826f26d00ff5ddf2de92cb6b2139b0dd3d1ee;
2675
2679
  };
2676
2680
  };
2677
2681
  6D0E635D28246BD0007C5DB6 /* XCRemoteSwiftPackageReference "Difference" */ = {
@@ -0,0 +1,22 @@
1
+ {
2
+ "pins" : [
3
+ {
4
+ "identity" : "difference",
5
+ "kind" : "remoteSourceControl",
6
+ "location" : "https://github.com/krzysztofzablocki/Difference",
7
+ "state" : {
8
+ "revision" : "02fe1111edc8318c4f8a0da96336fcbcc201f38b",
9
+ "version" : "1.0.1"
10
+ }
11
+ },
12
+ {
13
+ "identity" : "swift-snapshot-testing",
14
+ "kind" : "remoteSourceControl",
15
+ "location" : "https://github.com/pointfreeco/swift-snapshot-testing.git",
16
+ "state" : {
17
+ "revision" : "0c2826f26d00ff5ddf2de92cb6b2139b0dd3d1ee"
18
+ }
19
+ }
20
+ ],
21
+ "version" : 2
22
+ }
@@ -23,8 +23,8 @@
23
23
  "package": "swift-snapshot-testing",
24
24
  "repositoryURL": "https://github.com/pointfreeco/swift-snapshot-testing.git",
25
25
  "state": {
26
- "branch": "main",
27
- "revision": "88f6e2c0afe04221fcfb1601a2ecaad83115a05f",
26
+ "branch": null,
27
+ "revision": "0c2826f26d00ff5ddf2de92cb6b2139b0dd3d1ee",
28
28
  "version": null
29
29
  }
30
30
  }
@@ -18,5 +18,21 @@
18
18
  endingLineNumber = "44">
19
19
  </BreakpointContent>
20
20
  </BreakpointProxy>
21
+ <BreakpointProxy
22
+ BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
23
+ <BreakpointContent
24
+ uuid = "90B95B81-4B28-4BCD-AE7B-59ED8AA81AC1"
25
+ shouldBeEnabled = "No"
26
+ ignoreCount = "0"
27
+ continueAfterRunningActions = "No"
28
+ filePath = "Sources/Private/MainThread/NodeRenderSystem/NodeProperties/NodeProperty.swift"
29
+ startingColumnNumber = "9223372036854775807"
30
+ endingColumnNumber = "9223372036854775807"
31
+ startingLineNumber = "49"
32
+ endingLineNumber = "49"
33
+ landmarkName = "update(frame:)"
34
+ landmarkType = "7">
35
+ </BreakpointContent>
36
+ </BreakpointProxy>
21
37
  </Breakpoints>
22
38
  </Bucket>
@@ -56,22 +56,25 @@ extension CombinedShapeItem {
56
56
  /// Manually combines the given shape keyframes by manually interpolating at each frame
57
57
  static func manuallyInterpolating(
58
58
  shapes: [KeyframeGroup<BezierPath>],
59
- name: String,
60
- context: LayerContext)
59
+ name: String)
61
60
  -> CombinedShapeItem
62
61
  {
63
- let animationTimeRange = Int(context.animation.startFrame)...Int(context.animation.endFrame)
64
-
65
62
  let interpolators = shapes.map { shape in
66
63
  KeyframeInterpolator(keyframes: shape.keyframes)
67
64
  }
68
65
 
69
- let interpolatedKeyframes = animationTimeRange.map { frame in
66
+ let times = shapes.flatMap { $0.keyframes.map { $0.time } }
67
+
68
+ let minimumTime = times.min() ?? 0
69
+ let maximumTime = times.max() ?? 0
70
+ let animationLocalTimeRange = Int(minimumTime)...Int(maximumTime)
71
+
72
+ let interpolatedKeyframes = animationLocalTimeRange.map { localTime in
70
73
  Keyframe(
71
74
  value: interpolators.compactMap { interpolator in
72
- interpolator.value(frame: AnimationFrameTime(frame)) as? BezierPath
75
+ interpolator.value(frame: AnimationFrameTime(localTime)) as? BezierPath
73
76
  },
74
- time: AnimationFrameTime(frame))
77
+ time: AnimationFrameTime(localTime))
75
78
  }
76
79
 
77
80
  return CombinedShapeItem(
@@ -36,11 +36,23 @@ extension GradientRenderLayer {
36
36
  type: GradientContentType,
37
37
  context: LayerAnimationContext) throws
38
38
  {
39
- // We have to set `colors` to a non-nil value with some valid number of colors
40
- // for the color animation below to have any effect
41
- colors = .init(
42
- repeating: CGColor.rgb(0, 0, 0),
43
- count: gradient.numberOfColors)
39
+ // We have to set `colors` and `locations` to non-nil values
40
+ // for the animations below to actually take effect
41
+ locations = []
42
+
43
+ // The initial value for `colors` must be an array with the exact same number of colors
44
+ // as the gradient that will be applied in the `CAAnimation`
45
+ switch type {
46
+ case .rgb:
47
+ colors = .init(
48
+ repeating: CGColor.rgb(0, 0, 0),
49
+ count: gradient.numberOfColors)
50
+
51
+ case .alpha:
52
+ colors = .init(
53
+ repeating: CGColor.rgb(0, 0, 0),
54
+ count: gradient.colorConfiguration(from: gradient.colors.keyframes[0].value, type: .alpha).count)
55
+ }
44
56
 
45
57
  try addAnimation(
46
58
  for: .colors,
@@ -55,12 +55,13 @@ extension CAShapeLayer {
55
55
  try addOpacityAnimation(for: stroke, context: context)
56
56
 
57
57
  if let (dashPattern, dashPhase) = stroke.dashPattern?.shapeLayerConfiguration {
58
- lineDashPattern = try dashPattern.map {
58
+ let lineDashPattern = try dashPattern.map {
59
59
  try KeyframeGroup(keyframes: $0)
60
- .exactlyOneKeyframe(context: context, description: "stroke dashPattern").cgFloatValue as NSNumber
60
+ .exactlyOneKeyframe(context: context, description: "stroke dashPattern").cgFloatValue
61
61
  }
62
- if lineDashPattern?.allSatisfy({ $0.floatValue.isZero }) == true {
63
- lineDashPattern = nil
62
+
63
+ if lineDashPattern.isSupportedLayerDashPattern {
64
+ self.lineDashPattern = lineDashPattern as [NSNumber]
64
65
  }
65
66
 
66
67
  try addAnimation(
@@ -17,6 +17,7 @@ final class CoreAnimationLayer: BaseAnimationLayer {
17
17
  init(
18
18
  animation: Animation,
19
19
  imageProvider: AnimationImageProvider,
20
+ textProvider: AnimationTextProvider,
20
21
  fontProvider: AnimationFontProvider,
21
22
  compatibilityTrackerMode: CompatibilityTracker.Mode,
22
23
  logger: LottieLogger)
@@ -24,6 +25,7 @@ final class CoreAnimationLayer: BaseAnimationLayer {
24
25
  {
25
26
  self.animation = animation
26
27
  self.imageProvider = imageProvider
28
+ self.textProvider = textProvider
27
29
  self.fontProvider = fontProvider
28
30
  self.logger = logger
29
31
  compatibilityTracker = CompatibilityTracker(mode: compatibilityTrackerMode, logger: logger)
@@ -44,6 +46,7 @@ final class CoreAnimationLayer: BaseAnimationLayer {
44
46
  animation = typedLayer.animation
45
47
  currentAnimationConfiguration = typedLayer.currentAnimationConfiguration
46
48
  imageProvider = typedLayer.imageProvider
49
+ textProvider = typedLayer.textProvider
47
50
  fontProvider = typedLayer.fontProvider
48
51
  didSetUpAnimation = typedLayer.didSetUpAnimation
49
52
  compatibilityTracker = typedLayer.compatibilityTracker
@@ -92,6 +95,16 @@ final class CoreAnimationLayer: BaseAnimationLayer {
92
95
  didSet { reloadImages() }
93
96
  }
94
97
 
98
+ /// The `AnimationTextProvider` that `TextLayer`'s use to retrieve texts,
99
+ /// that they should use to render their text context
100
+ var textProvider: AnimationTextProvider {
101
+ didSet {
102
+ // We need to rebuild the current animation after updating the text provider,
103
+ // since this is used in `TextLayer.setupAnimations(context:)`
104
+ rebuildCurrentAnimation()
105
+ }
106
+ }
107
+
95
108
  /// The `FontProvider` that `TextLayer`s use to retrieve the `CTFont`
96
109
  /// that they should use to render their text content
97
110
  var fontProvider: AnimationFontProvider {
@@ -203,6 +216,7 @@ final class CoreAnimationLayer: BaseAnimationLayer {
203
216
  LayerContext(
204
217
  animation: animation,
205
218
  imageProvider: imageProvider,
219
+ textProvider: textProvider,
206
220
  fontProvider: fontProvider,
207
221
  compatibilityTracker: compatibilityTracker,
208
222
  layerName: "root layer")
@@ -234,6 +248,7 @@ final class CoreAnimationLayer: BaseAnimationLayer {
234
248
  compatibilityTracker: compatibilityTracker,
235
249
  logger: logger,
236
250
  currentKeypath: AnimationKeypath(keys: []),
251
+ textProvider: textProvider,
237
252
  logHierarchyKeypaths: configuration.logHierarchyKeypaths)
238
253
 
239
254
  // Perform a layout pass if necessary so all of the sublayers
@@ -314,11 +329,18 @@ extension CoreAnimationLayer: RootAnimationLayer {
314
329
  }
315
330
 
316
331
  var isAnimationPlaying: Bool? {
317
- switch playbackState {
332
+ switch pendingAnimationConfiguration?.playbackState {
318
333
  case .playing:
319
- return animation(forKey: #keyPath(animationProgress)) != nil
320
- case nil, .paused:
334
+ return true
335
+ case .paused:
321
336
  return false
337
+ case nil:
338
+ switch playbackState {
339
+ case .playing:
340
+ return animation(forKey: #keyPath(animationProgress)) != nil
341
+ case nil, .paused:
342
+ return false
343
+ }
322
344
  }
323
345
  }
324
346
 
@@ -383,15 +405,6 @@ extension CoreAnimationLayer: RootAnimationLayer {
383
405
  (sublayers ?? []).filter { $0 is AnimationLayer }
384
406
  }
385
407
 
386
- var textProvider: AnimationTextProvider {
387
- get { DictionaryTextProvider([:]) }
388
- set {
389
- logger.assertionFailure("""
390
- The Core Animation rendering engine currently doesn't support `textProvider`s")
391
- """)
392
- }
393
- }
394
-
395
408
  func reloadImages() {
396
409
  // When the image provider changes, we have to update all `ImageLayer`s
397
410
  // so they can query the most up-to-date image from the new image provider.
@@ -41,6 +41,9 @@ struct LayerAnimationContext {
41
41
  /// The AnimationKeypath represented by the current layer
42
42
  var currentKeypath: AnimationKeypath
43
43
 
44
+ /// The `AnimationTextProvider`
45
+ var textProvider: AnimationTextProvider
46
+
44
47
  /// Whether or not to log `AnimationKeypath`s for all of the animation's layers
45
48
  /// - Used for `CoreAnimationLayer.logHierarchyKeypaths()`
46
49
  var logHierarchyKeypaths: Bool
@@ -9,6 +9,7 @@ import QuartzCore
9
9
  struct LayerContext {
10
10
  let animation: Animation
11
11
  let imageProvider: AnimationImageProvider
12
+ let textProvider: AnimationTextProvider
12
13
  let fontProvider: AnimationFontProvider
13
14
  let compatibilityTracker: CompatibilityTracker
14
15
  var layerName: String
@@ -68,12 +68,12 @@ final class PreCompLayer: BaseCompositionLayer {
68
68
  try setupLayerAnimations(context: context)
69
69
 
70
70
  // Precomp layers can adjust the local time of their child layers (relative to the
71
- // animation's global time) via `timeRemapping` or a custom `startTime`
71
+ // animation's global time) via `timeRemapping` or a custom `startTime` / `timeStretch`
72
72
  let contextForChildren = context.withTimeRemapping { [preCompLayer, timeRemappingInterpolator] layerLocalFrame in
73
73
  if let timeRemappingInterpolator = timeRemappingInterpolator {
74
74
  return timeRemappingInterpolator.value(frame: layerLocalFrame) as? AnimationFrameTime ?? layerLocalFrame
75
75
  } else {
76
- return layerLocalFrame + AnimationFrameTime(preCompLayer.startTime)
76
+ return (layerLocalFrame * AnimationFrameTime(preCompLayer.timeStretch)) + AnimationFrameTime(preCompLayer.startTime)
77
77
  }
78
78
  }
79
79
 
@@ -157,7 +157,7 @@ final class GroupLayer: BaseAnimationLayer {
157
157
  // interpolate the path for each shape at each frame ahead of time so we can combine them
158
158
  // into a single set of bezier path keyframes.
159
159
  else {
160
- combinedShape = .manuallyInterpolating(shapes: allPathKeyframes, name: group.name, context: context)
160
+ combinedShape = .manuallyInterpolating(shapes: allPathKeyframes, name: group.name)
161
161
  }
162
162
 
163
163
  let sublayer = try ShapeItemLayer(
@@ -253,9 +253,9 @@ extension CALayer {
253
253
  pathForChildren.append(group.name)
254
254
  }
255
255
 
256
- let childItems = group.items.map {
257
- ShapeItemLayer.Item(item: $0, groupPath: pathForChildren)
258
- }
256
+ let childItems = group.items
257
+ .filter { !$0.hidden }
258
+ .map { ShapeItemLayer.Item(item: $0, groupPath: pathForChildren) }
259
259
 
260
260
  return try GroupLayer(
261
261
  group: group,
@@ -36,6 +36,21 @@ final class TextLayer: BaseCompositionLayer {
36
36
 
37
37
  // MARK: Internal
38
38
 
39
+ override func setupAnimations(context: LayerAnimationContext) throws {
40
+ try super.setupAnimations(context: context)
41
+ let textAnimationContext = context.addingKeypathComponent(textLayerModel.name)
42
+
43
+ let sourceText = try textLayerModel.text.exactlyOneKeyframe(
44
+ context: textAnimationContext,
45
+ description: "text layer text")
46
+
47
+ renderLayer.text = context.textProvider.textFor(
48
+ keypathName: textAnimationContext.currentKeypath.fullPath,
49
+ sourceText: sourceText.text)
50
+
51
+ renderLayer.sizeToFit()
52
+ }
53
+
39
54
  func configureRenderLayer(with context: LayerContext) throws {
40
55
  // We can't use `CATextLayer`, because it doesn't support enough features we use.
41
56
  // Instead, we use the same `CoreTextRenderLayer` (with a custom `draw` implementation)
@@ -54,7 +69,6 @@ final class TextLayer: BaseCompositionLayer {
54
69
  """)
55
70
  }
56
71
 
57
- renderLayer.text = text.text
58
72
  renderLayer.font = context.fontProvider.fontFor(family: text.fontFamily, size: CGFloat(text.fontSize))
59
73
 
60
74
  renderLayer.alignment = text.justification.textAlignment
@@ -80,7 +80,7 @@ extension Array where Element == ShapeItem {
80
80
  nodeTree.paths.append(contentsOf: tree.paths)
81
81
  nodeTree.renderContainers.append(node.container)
82
82
  } else if item is Repeater {
83
- LottieLogger.shared.assertionFailure("""
83
+ LottieLogger.shared.warn("""
84
84
  The Main Thread rendering engine doesn't currently support repeaters.
85
85
  To play an animation with repeaters, you can use the Core Animation rendering engine instead.
86
86
  """)
@@ -140,7 +140,7 @@ final class GradientStrokeNode: AnimatorNode, RenderNode {
140
140
 
141
141
  /// Get dash lengths
142
142
  let dashLengths = strokeProperties.dashPattern.value.map { $0.cgFloatValue }
143
- if dashLengths.count > 0, !dashLengths.allSatisfy({ $0.isZero }) {
143
+ if dashLengths.count > 0, dashLengths.isSupportedLayerDashPattern {
144
144
  strokeRender.strokeRender.dashPhase = strokeProperties.dashPhase.value.cgFloatValue
145
145
  strokeRender.strokeRender.dashLengths = dashLengths
146
146
  } else {
@@ -118,7 +118,7 @@ final class StrokeNode: AnimatorNode, RenderNode {
118
118
 
119
119
  /// Get dash lengths
120
120
  let dashLengths = strokeProperties.dashPattern.value.map { $0.cgFloatValue }
121
- if dashLengths.count > 0, !dashLengths.allSatisfy({ $0.isZero }) {
121
+ if dashLengths.count > 0, dashLengths.isSupportedLayerDashPattern {
122
122
  strokeRender.dashPhase = strokeProperties.dashPhase.value.cgFloatValue
123
123
  strokeRender.dashLengths = dashLengths
124
124
  } else {
@@ -167,3 +167,14 @@ extension Array where Element == DashElement {
167
167
  return (dashPatterns, dashPhase)
168
168
  }
169
169
  }
170
+
171
+ extension Array where Element == CGFloat {
172
+ // If all of the items in the dash pattern are zeros, then we shouldn't attempt to render it.
173
+ // This causes Core Animation to have extremely poor performance for some reason, even though
174
+ // it doesn't affect the appearance of the animation.
175
+ // - We check for `== 0.01` instead of `== 0` because `dashPattern.shapeLayerConfiguration`
176
+ // converts all `0` values to `0.01` to work around a different Core Animation rendering issue.
177
+ var isSupportedLayerDashPattern: Bool {
178
+ !allSatisfy { $0 == 0.01 }
179
+ }
180
+ }
@@ -15,6 +15,7 @@ final class Marker: Codable, DictionaryInitializable {
15
15
  init(dictionary: [String: Any]) throws {
16
16
  name = try dictionary.value(for: CodingKeys.name)
17
17
  frameTime = try dictionary.value(for: CodingKeys.frameTime)
18
+ durationFrameTime = try dictionary.value(for: CodingKeys.durationFrameTime)
18
19
  }
19
20
 
20
21
  // MARK: Internal
@@ -22,6 +23,7 @@ final class Marker: Codable, DictionaryInitializable {
22
23
  enum CodingKeys: String, CodingKey {
23
24
  case name = "cm"
24
25
  case frameTime = "tm"
26
+ case durationFrameTime = "dr"
25
27
  }
26
28
 
27
29
  /// The Marker Name
@@ -30,4 +32,6 @@ final class Marker: Codable, DictionaryInitializable {
30
32
  /// The Frame time of the marker
31
33
  let frameTime: AnimationFrameTime
32
34
 
35
+ /// The duration of the marker, in frames.
36
+ let durationFrameTime: AnimationFrameTime
33
37
  }
@@ -172,13 +172,14 @@ extension Animation {
172
172
  ///
173
173
  public static func loadedFrom(
174
174
  url: URL,
175
+ session: URLSession = .shared,
175
176
  closure: @escaping Animation.DownloadClosure,
176
177
  animationCache: AnimationCacheProvider?)
177
178
  {
178
179
  if let animationCache = animationCache, let animation = animationCache.animation(forKey: url.absoluteString) {
179
180
  closure(animation)
180
181
  } else {
181
- let task = URLSession.shared.dataTask(with: url) { data, _, error in
182
+ let task = session.dataTask(with: url) { data, _, error in
182
183
  guard error == nil, let jsonData = data else {
183
184
  DispatchQueue.main.async {
184
185
  closure(nil)
@@ -233,6 +234,21 @@ extension Animation {
233
234
  return marker.frameTime
234
235
  }
235
236
 
237
+ /// Markers are a way to describe a point in time and a duration by a key name.
238
+ ///
239
+ /// Markers are encoded into animation JSON. By using markers a designer can mark
240
+ /// playback points for a developer to use without having to worry about keeping
241
+ /// track of animation frames. If the animation file is updated, the developer
242
+ /// does not need to update playback code.
243
+ ///
244
+ /// - Returns: The duration frame time for the marker, or `nil` if no marker found.
245
+ public func durationFrameTime(forMarker named: String) -> AnimationFrameTime? {
246
+ guard let marker = markerMap?[named] else {
247
+ return nil
248
+ }
249
+ return marker.durationFrameTime
250
+ }
251
+
236
252
  /// Converts Frame Time (Seconds * Framerate) into Progress Time
237
253
  /// (optionally clamped to between 0 and 1).
238
254
  public func progressTime(
@@ -513,6 +513,32 @@ final public class AnimationView: AnimationViewBase {
513
513
  addNewAnimationForContext(context)
514
514
  }
515
515
 
516
+ /// Plays the animation from a named marker to the end of the marker's duration.
517
+ ///
518
+ /// A marker is a point in time with an associated duration that is encoded into the
519
+ /// animation data and assigned a name.
520
+ ///
521
+ /// NOTE: If marker is not found the play command will exit.
522
+ ///
523
+ /// - Parameter marker: The start marker for the animation playback.
524
+ /// - Parameter loopMode: The loop behavior of the animation. If `nil` the view's `loopMode` property will be used.
525
+ /// - Parameter completion: An optional completion closure to be called when the animation stops.
526
+ public func play(
527
+ marker: String,
528
+ loopMode: LottieLoopMode? = nil,
529
+ completion: LottieCompletionBlock? = nil)
530
+ {
531
+ guard let from = animation?.markerMap?[marker] else {
532
+ return
533
+ }
534
+
535
+ play(
536
+ fromFrame: from.frameTime,
537
+ toFrame: from.frameTime + from.durationFrameTime,
538
+ loopMode: loopMode,
539
+ completion: completion)
540
+ }
541
+
516
542
  /// Stops the animation and resets the view to its start frame.
517
543
  ///
518
544
  /// The completion closure will be called with `false`
@@ -713,6 +739,21 @@ final public class AnimationView: AnimationViewBase {
713
739
  return animation.frameTime(forMarker: named)
714
740
  }
715
741
 
742
+ /// Markers are a way to describe a point in time and a duration by a key name.
743
+ ///
744
+ /// Markers are encoded into animation JSON. By using markers a designer can mark
745
+ /// playback points for a developer to use without having to worry about keeping
746
+ /// track of animation frames. If the animation file is updated, the developer
747
+ /// does not need to update playback code.
748
+ ///
749
+ /// - Returns: The duration frame time for the marker, or `nil` if no marker found.
750
+ public func durationFrameTime(forMarker named: String) -> AnimationFrameTime? {
751
+ guard let animation = animation else {
752
+ return nil
753
+ }
754
+ return animation.durationFrameTime(forMarker: named)
755
+ }
756
+
716
757
  // MARK: Internal
717
758
 
718
759
  var animationLayer: RootAnimationLayer? = nil
@@ -996,6 +1037,7 @@ final public class AnimationView: AnimationViewBase {
996
1037
  let coreAnimationLayer = try CoreAnimationLayer(
997
1038
  animation: animation,
998
1039
  imageProvider: imageProvider.cachedImageProvider,
1040
+ textProvider: textProvider,
999
1041
  fontProvider: fontProvider,
1000
1042
  compatibilityTrackerMode: .track,
1001
1043
  logger: logger)
@@ -1030,6 +1072,7 @@ final public class AnimationView: AnimationViewBase {
1030
1072
  let coreAnimationLayer = try CoreAnimationLayer(
1031
1073
  animation: animation,
1032
1074
  imageProvider: imageProvider.cachedImageProvider,
1075
+ textProvider: textProvider,
1033
1076
  fontProvider: fontProvider,
1034
1077
  compatibilityTrackerMode: .abort,
1035
1078
  logger: logger)
@@ -31,29 +31,9 @@ open class AnimatedButton: AnimatedControl {
31
31
  isAccessibilityElement = true
32
32
  }
33
33
 
34
- // MARK: Public
35
-
36
- public override var accessibilityTraits: UIAccessibilityTraits {
37
- set { super.accessibilityTraits = newValue }
38
- get { super.accessibilityTraits.union(.button) }
39
- }
40
-
41
- /// Sets the play range for the given UIControlEvent.
42
- public func setPlayRange(fromProgress: AnimationProgressTime, toProgress: AnimationProgressTime, event: UIControl.Event) {
43
- rangesForEvents[event.rawValue] = (from: fromProgress, to: toProgress)
44
- }
34
+ // MARK: Open
45
35
 
46
- /// Sets the play range for the given UIControlEvent.
47
- public func setPlayRange(fromMarker fromName: String, toMarker toName: String, event: UIControl.Event) {
48
- if
49
- let start = animationView.progressTime(forMarker: fromName),
50
- let end = animationView.progressTime(forMarker: toName)
51
- {
52
- rangesForEvents[event.rawValue] = (from: start, to: end)
53
- }
54
- }
55
-
56
- public override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
36
+ open override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
57
37
  let _ = super.beginTracking(touch, with: event)
58
38
  let touchEvent = UIControl.Event.touchDown
59
39
  if let playrange = rangesForEvents[touchEvent.rawValue] {
@@ -62,7 +42,7 @@ open class AnimatedButton: AnimatedControl {
62
42
  return true
63
43
  }
64
44
 
65
- public override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
45
+ open override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
66
46
  super.endTracking(touch, with: event)
67
47
  let touchEvent: UIControl.Event
68
48
  if let touch = touch, bounds.contains(touch.location(in: self)) {
@@ -76,6 +56,28 @@ open class AnimatedButton: AnimatedControl {
76
56
  }
77
57
  }
78
58
 
59
+ // MARK: Public
60
+
61
+ public override var accessibilityTraits: UIAccessibilityTraits {
62
+ set { super.accessibilityTraits = newValue }
63
+ get { super.accessibilityTraits.union(.button) }
64
+ }
65
+
66
+ /// Sets the play range for the given UIControlEvent.
67
+ public func setPlayRange(fromProgress: AnimationProgressTime, toProgress: AnimationProgressTime, event: UIControl.Event) {
68
+ rangesForEvents[event.rawValue] = (from: fromProgress, to: toProgress)
69
+ }
70
+
71
+ /// Sets the play range for the given UIControlEvent.
72
+ public func setPlayRange(fromMarker fromName: String, toMarker toName: String, event: UIControl.Event) {
73
+ if
74
+ let start = animationView.progressTime(forMarker: fromName),
75
+ let end = animationView.progressTime(forMarker: toName)
76
+ {
77
+ rangesForEvents[event.rawValue] = (from: start, to: end)
78
+ }
79
+ }
80
+
79
81
  // MARK: Private
80
82
 
81
83
  private var rangesForEvents: [UInt : (from: CGFloat, to: CGFloat)] =