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
@@ -0,0 +1,148 @@
1
+ // Created by eric_horacek on 9/8/22.
2
+ // Copyright © 2022 Airbnb Inc. All rights reserved.
3
+
4
+ import SwiftUI
5
+
6
+ // MARK: - SwiftUIView
7
+
8
+ /// A `UIViewRepresentable` SwiftUI `View` that wraps its `Content` `UIView` within a
9
+ /// `SwiftUIMeasurementContainer`, used to size a UIKit view correctly within a SwiftUI view
10
+ /// hierarchy.
11
+ ///
12
+ /// Includes an optional generic `Storage` value, which can be used to compare old and new values
13
+ /// across state changes to prevent redundant view updates.
14
+ @available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
15
+ struct SwiftUIView<Content: ViewType, Storage>: MeasuringViewRepresentable,
16
+ UIViewConfiguringSwiftUIView
17
+ {
18
+
19
+ // MARK: Lifecycle
20
+
21
+ /// Creates a SwiftUI representation of the content view with the given storage and the provided
22
+ /// `makeContent` closure to construct the content whenever `makeUIView(…)` is invoked.
23
+ init(storage: Storage, makeContent: @escaping () -> Content) {
24
+ self.storage = storage
25
+ self.makeContent = makeContent
26
+ }
27
+
28
+ /// Creates a SwiftUI representation of the content view with the provided `makeContent` closure
29
+ /// to construct it whenever `makeUIView(…)` is invoked.
30
+ init(makeContent: @escaping () -> Content) where Storage == Void {
31
+ storage = ()
32
+ self.makeContent = makeContent
33
+ }
34
+
35
+ // MARK: Internal
36
+
37
+ var configurations: [Configuration] = []
38
+
39
+ var sizing: SwiftUIMeasurementContainerStrategy = .automatic
40
+
41
+ // MARK: Private
42
+
43
+ /// The current stored value, with the previous value provided to the configuration closure as
44
+ /// the `oldStorage`.
45
+ private var storage: Storage
46
+
47
+ /// A closure that's invoked to construct the represented content view.
48
+ private var makeContent: () -> Content
49
+ }
50
+
51
+ // MARK: UIViewRepresentable
52
+
53
+ @available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
54
+ extension SwiftUIView {
55
+ func makeCoordinator() -> Coordinator {
56
+ Coordinator(storage: storage)
57
+ }
58
+
59
+ #if os(macOS)
60
+ func makeNSView(context _: Context) -> SwiftUIMeasurementContainer<Content> {
61
+ SwiftUIMeasurementContainer(content: makeContent(), strategy: sizing)
62
+ }
63
+
64
+ func updateNSView(_ uiView: SwiftUIMeasurementContainer<Content>, context: Context) {
65
+ let oldStorage = context.coordinator.storage
66
+ context.coordinator.storage = storage
67
+
68
+ let configurationContext = ConfigurationContext(
69
+ oldStorage: oldStorage,
70
+ viewRepresentableContext: context,
71
+ container: uiView)
72
+
73
+ for configuration in configurations {
74
+ configuration(configurationContext)
75
+ }
76
+ }
77
+ #else
78
+ func makeUIView(context _: Context) -> SwiftUIMeasurementContainer<Content> {
79
+ SwiftUIMeasurementContainer(content: makeContent(), strategy: sizing)
80
+ }
81
+
82
+ func updateUIView(_ uiView: SwiftUIMeasurementContainer<Content>, context: Context) {
83
+ let oldStorage = context.coordinator.storage
84
+ context.coordinator.storage = storage
85
+
86
+ let configurationContext = ConfigurationContext(
87
+ oldStorage: oldStorage,
88
+ viewRepresentableContext: context,
89
+ container: uiView)
90
+
91
+ for configuration in configurations {
92
+ configuration(configurationContext)
93
+ }
94
+ }
95
+ #endif
96
+ }
97
+
98
+ // MARK: SwiftUIView.ConfigurationContext
99
+
100
+ @available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
101
+ extension SwiftUIView {
102
+ /// The configuration context that's available to configure the `Content` view whenever the
103
+ /// `updateUIView()` method is invoked via a configuration closure.
104
+ struct ConfigurationContext: ViewProviding {
105
+ /// The previous value for the `Storage` of this `SwiftUIView`, which can be used to store
106
+ /// values across state changes to prevent redundant view updates.
107
+ var oldStorage: Storage
108
+
109
+ /// The `UIViewRepresentable.Context`, with information about the transaction and environment.
110
+ var viewRepresentableContext: Context
111
+
112
+ /// The backing measurement container that contains the `Content`.
113
+ var container: SwiftUIMeasurementContainer<Content>
114
+
115
+ /// The `UIView` content that's being configured.
116
+ ///
117
+ /// Setting this to a new value updates the backing measurement container's `content`.
118
+ var view: Content {
119
+ get { container.content }
120
+ nonmutating set { container.content = newValue }
121
+ }
122
+
123
+ /// A convenience accessor indicating whether this content update should be animated.
124
+ var animated: Bool {
125
+ viewRepresentableContext.transaction.animation != nil
126
+ }
127
+ }
128
+ }
129
+
130
+ // MARK: SwiftUIView.Coordinator
131
+
132
+ @available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
133
+ extension SwiftUIView {
134
+ /// A coordinator that stores the `storage` associated with this view, enabling the old storage
135
+ /// value to be accessed during the `updateUIView(…)`.
136
+ final class Coordinator {
137
+
138
+ // MARK: Lifecycle
139
+
140
+ fileprivate init(storage: Storage) {
141
+ self.storage = storage
142
+ }
143
+
144
+ // MARK: Internal
145
+
146
+ fileprivate(set) var storage: Storage
147
+ }
148
+ }
@@ -0,0 +1,40 @@
1
+ // Created by eric_horacek on 3/3/22.
2
+ // Copyright © 2022 Airbnb Inc. All rights reserved.
3
+
4
+ import SwiftUI
5
+
6
+ // MARK: - ViewTypeProtocol + swiftUIView
7
+
8
+ @available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
9
+ extension ViewTypeProtocol {
10
+ /// Returns a SwiftUI `View` representing this `UIView`, constructed with the given `makeView`
11
+ /// closure and sized with the given sizing configuration.
12
+ ///
13
+ /// To perform additional configuration of the `UIView` instance, call `configure` on the
14
+ /// returned SwiftUI `View`:
15
+ /// ```
16
+ /// MyUIView.swiftUIView(…)
17
+ /// .configure { context in
18
+ /// context.view.doSomething()
19
+ /// }
20
+ /// ```
21
+ ///
22
+ /// To configure the sizing behavior of the `UIView` instance, call `sizing` on the returned
23
+ /// SwiftUI `View`:
24
+ /// ```
25
+ /// MyView.swiftUIView(…).sizing(.intrinsicSize)
26
+ /// ```
27
+ /// The sizing defaults to `.automatic`.
28
+ static func swiftUIView(makeView: @escaping () -> Self) -> SwiftUIView<Self, Void> {
29
+ SwiftUIView(makeContent: makeView)
30
+ }
31
+ }
32
+
33
+ // MARK: - ViewTypeProtocol
34
+
35
+ /// A protocol that all `UIView`s conform to, enabling extensions that have a `Self` reference.
36
+ protocol ViewTypeProtocol: ViewType { }
37
+
38
+ // MARK: - ViewType + ViewTypeProtocol
39
+
40
+ extension ViewType: ViewTypeProtocol { }
@@ -0,0 +1,43 @@
1
+ // Created by eric_horacek on 3/4/22.
2
+ // Copyright © 2022 Airbnb Inc. All rights reserved.
3
+
4
+ import SwiftUI
5
+
6
+ // MARK: - UIViewConfiguringSwiftUIView
7
+
8
+ /// A protocol describing a SwiftUI `View` that can configure its `UIView` content via an array of
9
+ /// `configuration` closures.
10
+ @available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
11
+ protocol UIViewConfiguringSwiftUIView: View {
12
+ /// The context available to this configuration, which provides the `UIView` instance at a minimum
13
+ /// but can include additional context as needed.
14
+ associatedtype ConfigurationContext: ViewProviding
15
+
16
+ /// A closure that is invoked to configure the represented content view.
17
+ typealias Configuration = (ConfigurationContext) -> Void
18
+
19
+ /// A mutable array of configuration closures that should each be invoked with the
20
+ /// `ConfigurationContext` whenever `updateUIView` is called in a `UIViewRepresentable`.
21
+ var configurations: [Configuration] { get set }
22
+ }
23
+
24
+ // MARK: Extensions
25
+
26
+ @available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
27
+ extension UIViewConfiguringSwiftUIView {
28
+ /// Returns a copy of this view updated to have the given closure applied to its represented view
29
+ /// whenever it is updated via the `updateUIView(…)` method.
30
+ func configure(_ configure: @escaping Configuration) -> Self {
31
+ var copy = self
32
+ copy.configurations.append(configure)
33
+ return copy
34
+ }
35
+
36
+ /// Returns a copy of this view updated to have the given closures applied to its represented view
37
+ /// whenever it is updated via the `updateUIView(…)` method.
38
+ func configurations(_ configurations: [Configuration]) -> Self {
39
+ var copy = self
40
+ copy.configurations.append(contentsOf: configurations)
41
+ return copy
42
+ }
43
+ }
@@ -0,0 +1,45 @@
1
+ // Created by Tyler Hedrick on 5/26/20.
2
+ // Copyright © 2020 Airbnb Inc. All rights reserved.
3
+
4
+ // MARK: - BehaviorsConfigurableView
5
+
6
+ /// A view that can be configured with a `Behaviors` instance that contains the view's non-
7
+ /// `Equatable` properties that can be updated on view instances after initialization, e.g. callback
8
+ /// closures or delegates.
9
+ ///
10
+ /// Since it is not possible to establish the equality of two `Behaviors` instances, `Behaviors`
11
+ /// will be set more often than `ContentConfigurableView.Content`, needing to be updated every time
12
+ /// the view's corresponding `EpoxyModeled` instance is updated. As such, setting behaviors should
13
+ /// be as lightweight as possible.
14
+ ///
15
+ /// Properties of `Behaviors` should be mutually exclusive with the properties in the
16
+ /// `StyledView.Style` and `ContentConfigurableView.Content`.
17
+ ///
18
+ /// - SeeAlso: `ContentConfigurableView`
19
+ /// - SeeAlso: `StyledView`
20
+ /// - SeeAlso: `EpoxyableView`
21
+ protocol BehaviorsConfigurableView: ViewType {
22
+ /// The non-`Equatable` properties that can be changed over of the lifecycle this View's
23
+ /// instances, e.g. callback closures or delegates.
24
+ ///
25
+ /// Defaults to `Never` for views that do not have `Behaviors`.
26
+ associatedtype Behaviors = Never
27
+
28
+ /// Updates the behaviors of this view to those in the given `behaviors`, else resets the
29
+ /// behaviors if `nil`.
30
+ ///
31
+ /// Behaviors are optional as they must be "resettable" in order for Epoxy to reset the behaviors
32
+ /// on your view when no behaviors are provided.
33
+ func setBehaviors(_ behaviors: Self.Behaviors?)
34
+ }
35
+
36
+ // MARK: Defaults
37
+
38
+ extension BehaviorsConfigurableView where Behaviors == Never {
39
+ func setBehaviors(_ behaviors: Never?) {
40
+ switch behaviors {
41
+ case nil:
42
+ break
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,36 @@
1
+ // Created by Laura Skelton on 5/30/17.
2
+ // Copyright © 2017 Airbnb. All rights reserved.
3
+
4
+ // MARK: - ContentConfigurableView
5
+
6
+ /// A view that can be configured with a `Content` instance that contains the view's `Equatable`
7
+ /// properties that can be updated on existing view instances, e.g. text `String`s or image `URL`s.
8
+ ///
9
+ /// For performance, it is generally expected that `Content` is only set when it is not equal to the
10
+ /// previous `Content` instance that has been set on a view instance. As a further optimization,
11
+ /// this view can guard updates on the equality of each property of the `Content` against the
12
+ /// current property value when set.
13
+ ///
14
+ /// Properties of `Content` should be mutually exclusive with the properties of the
15
+ /// `StyledView.Style` and `BehaviorsConfigurableView.Behaviors`.
16
+ ///
17
+ /// - SeeAlso: `BehaviorsConfigurableView`
18
+ /// - SeeAlso: `StyledView`
19
+ /// - SeeAlso: `EpoxyableView`
20
+ protocol ContentConfigurableView: ViewType {
21
+ /// The `Equatable` properties that can be updated on instances of this view, e.g. text `String`s
22
+ /// or image `URL`s.
23
+ ///
24
+ /// Defaults to `Never` for views that do not have `Content`.
25
+ associatedtype Content: Equatable = Never
26
+
27
+ /// Updates the content of this view to the properties of the given `content`, optionally
28
+ /// animating the updates.
29
+ func setContent(_ content: Self.Content, animated: Bool)
30
+ }
31
+
32
+ // MARK: Defaults
33
+
34
+ extension ContentConfigurableView where Content == Never {
35
+ func setContent(_: Never, animated _: Bool) { }
36
+ }
@@ -0,0 +1,5 @@
1
+ // Created by eric_horacek on 1/13/21.
2
+ // Copyright © 2021 Airbnb Inc. All rights reserved.
3
+
4
+ /// A `UIView` that can be declaratively configured via a concrete `EpoxyableModel` instance.
5
+ typealias EpoxyableView = BehaviorsConfigurableView & ContentConfigurableView & StyledView
@@ -0,0 +1,42 @@
1
+ // Created by Laura Skelton on 4/14/16.
2
+ // Copyright © 2016 Airbnb. All rights reserved.
3
+
4
+ // MARK: - StyledView
5
+
6
+ /// A view that can be initialized with a `Style` instance that contains the view's invariant
7
+ /// configuration parameters, e.g. the `UIButton.ButtonType` of a `UIButton`.
8
+ ///
9
+ /// A `Style` is expected to be invariant over the lifecycle of the view; it should not possible to
10
+ /// change the `Style` of a view after it is created. All variant properties of the view should
11
+ /// either be included in the `ContentConfigurableView.Content` if they are `Equatable` (e.g. a
12
+ /// title `String`) or the `BehaviorsConfigurableView.Behaviors` if they are not (e.g. a callback
13
+ /// closure).
14
+ ///
15
+ /// A `Style` is `Hashable` to allow views of the same type with equal `Style`s to be reused by
16
+ /// establishing whether their invariant `Style` instances are equal.
17
+ ///
18
+ /// Properties of `Style` should be mutually exclusive with the properties of the
19
+ /// `ContentConfigurableView.Content` and `BehaviorsConfigurableView.Behaviors`.
20
+ ///
21
+ /// - SeeAlso: `ContentConfigurableView`
22
+ /// - SeeAlso: `BehaviorsConfigurableView`
23
+ /// - SeeAlso: `EpoxyableView`
24
+ protocol StyledView: ViewType {
25
+ /// The style type of this view, passed into its initializer to configure the resulting instance.
26
+ ///
27
+ /// Defaults to `Never` for views that do not have a `Style`.
28
+ associatedtype Style: Hashable = Never
29
+
30
+ /// Creates an instance of this view configured with the given `Style` instance.
31
+ init(style: Style)
32
+ }
33
+
34
+ // MARK: Defaults
35
+
36
+ extension StyledView where Style == Never {
37
+ init(style: Never) {
38
+ // An empty switch is required to silence the "'self.init' isn't called on all paths before
39
+ // returning from initializer" error.
40
+ switch style { }
41
+ }
42
+ }
@@ -0,0 +1,51 @@
1
+ // Created by Cal Stephens on 6/26/23.
2
+ // Copyright © 2023 Airbnb Inc. All rights reserved.
3
+
4
+ import SwiftUI
5
+
6
+ #if canImport(UIKit)
7
+ import UIKit
8
+
9
+ /// The platform's main view type.
10
+ /// Either `UIView` on iOS/tvOS or `NSView` on macOS.
11
+ typealias ViewType = UIView
12
+
13
+ /// The platform's SwiftUI view representable type.
14
+ /// Either `UIViewRepresentable` on iOS/tvOS or `NSViewRepresentable` on macOS.
15
+ @available(iOS 13.0, tvOS 13.0, *)
16
+ typealias ViewRepresentableType = UIViewRepresentable
17
+
18
+ /// The platform's layout constraint priority type.
19
+ /// Either `UILayoutPriority` on iOS/tvOS or `NSLayoutConstraint.Priority` on macOS.
20
+ typealias LayoutPriorityType = UILayoutPriority
21
+
22
+ @available(iOS 13.0, tvOS 13.0, *)
23
+ extension ViewRepresentableType {
24
+ /// The platform's view type for `ViewRepresentableType`.
25
+ /// Either `UIViewType` on iOS/tvOS or `NSViewType` on macOS.
26
+ typealias RepresentableViewType = UIViewType
27
+ }
28
+
29
+ #elseif canImport(AppKit)
30
+ import AppKit
31
+
32
+ /// The platform's main view type.
33
+ /// Either `UIView` on iOS/tvOS, or `NSView` on macOS.
34
+ typealias ViewType = NSView
35
+
36
+ /// The platform's SwiftUI view representable type.
37
+ /// Either `UIViewRepresentable` on iOS/tvOS, or `NSViewRepresentable` on macOS.
38
+ @available(macOS 10.15, *)
39
+ typealias ViewRepresentableType = NSViewRepresentable
40
+
41
+ /// The platform's layout constraint priority type.
42
+ /// Either `UILayoutPriority` on iOS/tvOS, or `NSLayoutConstraint.Priority` on macOS.
43
+ typealias LayoutPriorityType = NSLayoutConstraint.Priority
44
+
45
+ @available(macOS 10.15, *)
46
+ extension ViewRepresentableType {
47
+ /// The platform's view type for `ViewRepresentableType`.
48
+ /// Either `UIViewType` on iOS/tvOS or `NSViewType` on macOS.
49
+ typealias RepresentableViewType = NSViewType
50
+ }
51
+ #endif
@@ -0,0 +1,27 @@
1
+ ## ZipFoundation
2
+
3
+ This directory includes the source code of libraries that are embedded within lottie-ios.
4
+
5
+ This includes:
6
+ - ZipFoundation (https://github.com/weichsel/ZIPFoundation)
7
+ - EpoxyCore (https://github.com/airbnb/epoxy-ios)
8
+
9
+ Lottie is distributed via multiple package managers (SPM, Cocoapods, Carthage, and NPM),
10
+ each with different packaging and compilation requirements.
11
+
12
+ Due to limitations of these package managers, we can't depend on / import
13
+ a separates modules / libraries. Instead, we include the source
14
+ directly within the Lottie library and compile everything as a single unit.
15
+
16
+ ### Update instructions
17
+
18
+ From time to time we may need to update to a more recent version of one of these libraries.
19
+ When doing this, follow these steps:
20
+
21
+ 1. Download the latest release of the library and replace the source code in
22
+ the corresponding directory with the updated code.
23
+
24
+ 2. Update the URL in the directory's README.md to indicate what release is being used.
25
+
26
+ 3. Change all of the `public` symbols defined in the module to instead be `internal`
27
+ to prevent Lottie from exposing any APIs from other libraries.
@@ -54,8 +54,8 @@ extension Archive {
54
54
  let fileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
55
55
  guard
56
56
  let archiveFile = fopen(fileSystemRepresentation, "rb"),
57
- let (eocdRecord, zip64EOCD) = Archive.scanForEndOfCentralDirectoryRecord(in: archiveFile) else
58
- {
57
+ let (eocdRecord, zip64EOCD) = Archive.scanForEndOfCentralDirectoryRecord(in: archiveFile)
58
+ else {
59
59
  return nil
60
60
  }
61
61
  return BackingConfiguration(
@@ -80,8 +80,8 @@ extension Archive {
80
80
  let fileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
81
81
  guard
82
82
  let archiveFile = fopen(fileSystemRepresentation, "rb+"),
83
- let (eocdRecord, zip64EOCD) = Archive.scanForEndOfCentralDirectoryRecord(in: archiveFile) else
84
- {
83
+ let (eocdRecord, zip64EOCD) = Archive.scanForEndOfCentralDirectoryRecord(in: archiveFile)
84
+ else {
85
85
  return nil
86
86
  }
87
87
  fseeko(archiveFile, 0, SEEK_SET)
@@ -37,7 +37,7 @@ class MemoryFile {
37
37
  let cookie = Unmanaged.passRetained(self)
38
38
  let writable = mode.count > 0 && (mode.first! != "r" || mode.last! == "+")
39
39
  let append = mode.count > 0 && mode.first! == "a"
40
- #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(Android)
40
+ #if os(macOS) || canImport(UIKit) || os(Android)
41
41
  let result = writable
42
42
  ? funopen(cookie.toOpaque(), readStub, writeStub, seekStub, closeStub)
43
43
  : funopen(cookie.toOpaque(), readStub, nil, seekStub, closeStub)
@@ -108,7 +108,7 @@ private func closeStub(_ cookie: UnsafeMutableRawPointer?) -> Int32 {
108
108
  return 0
109
109
  }
110
110
 
111
- #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(Android)
111
+ #if os(macOS) || canImport(UIKit) || os(Android)
112
112
  private func readStub(
113
113
  _ cookie: UnsafeMutableRawPointer?,
114
114
  _ bytePtr: UnsafeMutablePointer<Int8>?,
@@ -281,8 +281,8 @@ extension Archive {
281
281
  #if swift(>=5.0)
282
282
  guard
283
283
  let data = archive.data,
284
- let config = Archive.makeBackingConfiguration(for: data, mode: .update) else
285
- {
284
+ let config = Archive.makeBackingConfiguration(for: data, mode: .update)
285
+ else {
286
286
  throw ArchiveError.unwritableArchive
287
287
  }
288
288
  archiveFile = config.file
@@ -360,8 +360,8 @@ extension Archive {
360
360
  let tempArchive = Archive(
361
361
  data: Data(),
362
362
  accessMode: .create,
363
- preferredEncoding: preferredEncoding) else
364
- {
363
+ preferredEncoding: preferredEncoding)
364
+ else {
365
365
  throw ArchiveError.unwritableArchive
366
366
  }
367
367
  archive = tempArchive
@@ -206,8 +206,8 @@ final class Archive: Sequence {
206
206
  init?(data: Data = Data(), accessMode mode: AccessMode, preferredEncoding: String.Encoding? = nil) {
207
207
  guard
208
208
  let url = URL(string: "\(memoryURLScheme)://"),
209
- let config = Archive.makeBackingConfiguration(for: data, mode: mode) else
210
- {
209
+ let config = Archive.makeBackingConfiguration(for: data, mode: mode)
210
+ else {
211
211
  return nil
212
212
  }
213
213
 
@@ -256,15 +256,16 @@ final class Archive: Sequence {
256
256
  guard
257
257
  let centralDirStruct: CentralDirectoryStructure = Data.readStruct(
258
258
  from: self.archiveFile,
259
- at: directoryIndex) else
260
- {
259
+ at: directoryIndex)
260
+ else {
261
261
  return nil
262
262
  }
263
263
  let offset = UInt64(centralDirStruct.effectiveRelativeOffsetOfLocalHeader)
264
264
  guard
265
265
  let localFileHeader: LocalFileHeader = Data.readStruct(
266
266
  from: self.archiveFile,
267
- at: offset) else { return nil }
267
+ at: offset)
268
+ else { return nil }
268
269
  var dataDescriptor: DataDescriptor?
269
270
  var zip64DataDescriptor: ZIP64DataDescriptor?
270
271
  if centralDirStruct.usesDataDescriptor {
@@ -326,8 +327,8 @@ final class Archive: Sequence {
326
327
  let recordOffset = locatorOffset - UInt64(ZIP64EndOfCentralDirectoryRecord.size)
327
328
  guard
328
329
  let locator: ZIP64EndOfCentralDirectoryLocator = Data.readStruct(from: file, at: locatorOffset),
329
- let record: ZIP64EndOfCentralDirectoryRecord = Data.readStruct(from: file, at: recordOffset) else
330
- {
330
+ let record: ZIP64EndOfCentralDirectoryRecord = Data.readStruct(from: file, at: recordOffset)
331
+ else {
331
332
  return nil
332
333
  }
333
334
  return ZIP64EndOfCentralDirectory(record: record, locator: locator)
@@ -53,7 +53,7 @@ extension Data {
53
53
  /// - consumer: A closure that processes the result of the compress operation.
54
54
  /// - Returns: The checksum of the processed content.
55
55
  static func compress(size: Int64, bufferSize: Int, provider: Provider, consumer: Consumer) throws -> CRC32 {
56
- #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
56
+ #if os(macOS) || canImport(UIKit)
57
57
  return try process(
58
58
  operation: COMPRESSION_STREAM_ENCODE,
59
59
  size: size,
@@ -81,7 +81,7 @@ extension Data {
81
81
  consumer: Consumer)
82
82
  throws -> CRC32
83
83
  {
84
- #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
84
+ #if os(macOS) || canImport(UIKit)
85
85
  return try process(
86
86
  operation: COMPRESSION_STREAM_DECODE,
87
87
  size: size,
@@ -113,7 +113,7 @@ extension Data {
113
113
 
114
114
  // MARK: - Apple Platforms
115
115
 
116
- #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
116
+ #if os(macOS) || canImport(UIKit)
117
117
  import Compression
118
118
 
119
119
  extension Data {
@@ -277,8 +277,8 @@ extension Data {
277
277
  guard
278
278
  result != Z_NEED_DICT,
279
279
  result != Z_DATA_ERROR,
280
- result != Z_MEM_ERROR else
281
- {
280
+ result != Z_MEM_ERROR
281
+ else {
282
282
  throw CompressionError.corruptedData
283
283
  }
284
284
  }
@@ -81,7 +81,7 @@ extension FileManager {
81
81
  let entryFileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
82
82
  var fileStat = stat()
83
83
  lstat(entryFileSystemRepresentation, &fileStat)
84
- #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
84
+ #if os(macOS) || canImport(UIKit)
85
85
  let modTimeSpec = fileStat.st_mtimespec
86
86
  #else
87
87
  let modTimeSpec = fileStat.st_mtim
@@ -307,7 +307,7 @@ extension Date {
307
307
  // MARK: Internal
308
308
 
309
309
  var fileModificationDateTime: (UInt16, UInt16) {
310
- return (self.fileModificationDate, self.fileModificationTime)
310
+ (self.fileModificationDate, self.fileModificationTime)
311
311
  }
312
312
 
313
313
  var fileModificationDate: UInt16 {
@@ -321,7 +321,7 @@ extension Date {
321
321
  year = year <= 2099 ? year : 2099
322
322
  let month = unixTime.pointee.tm_mon + 1 // UNIX time struct month entries are zero based.
323
323
  let day = unixTime.pointee.tm_mday
324
- return (UInt16)(day + (month * 32) + ((year - 1980) * 512))
324
+ return UInt16(day + (month * 32) + ((year - 1980) * 512))
325
325
  }
326
326
 
327
327
  var fileModificationTime: UInt16 {
@@ -332,7 +332,7 @@ extension Date {
332
332
  let hour = unixTime.pointee.tm_hour
333
333
  let minute = unixTime.pointee.tm_min
334
334
  let second = unixTime.pointee.tm_sec
335
- return (UInt16)((second / 2) + (minute * 32) + (hour * 2048))
335
+ return UInt16((second / 2) + (minute * 32) + (hour * 2048))
336
336
  }
337
337
  }
338
338
 
@@ -47,4 +47,10 @@ final class ImageCompositionLayer: CompositionLayer {
47
47
  }
48
48
  }
49
49
  }
50
+
51
+ var imageContentsGravity: CALayerContentsGravity = .resize {
52
+ didSet {
53
+ contentsLayer.contentsGravity = imageContentsGravity
54
+ }
55
+ }
50
56
  }
@@ -73,7 +73,9 @@ final class MaskContainerLayer: CALayer {
73
73
  // MARK: Internal
74
74
 
75
75
  func updateWithFrame(frame: CGFloat, forceUpdates: Bool) {
76
- maskLayers.forEach { $0.updateWithFrame(frame: frame, forceUpdates: forceUpdates) }
76
+ for maskLayer in maskLayers {
77
+ maskLayer.updateWithFrame(frame: frame, forceUpdates: forceUpdates)
78
+ }
77
79
  }
78
80
 
79
81
  // MARK: Fileprivate