lottie-ios 3.2.2 → 3.4.1
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.
- package/.github/actions/setup/action.yml +32 -0
- package/.github/issue_template.md +6 -23
- package/.github/workflows/main.yml +98 -0
- package/.spi.yml +6 -0
- package/.swift-version +1 -0
- package/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +7 -0
- package/.swiftpm/xcode/package.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/.swiftpm/xcode/xcuserdata/{brandonwithrow.xcuserdatad → cal.xcuserdatad}/xcschemes/xcschememanagement.plist +25 -15
- package/.swiftpm/xcode/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/Gemfile +4 -0
- package/Gemfile.lock +102 -0
- package/Lottie.xcodeproj/project.pbxproj +2003 -1972
- package/Lottie.xcodeproj/project.xcworkspace/contents.xcworkspacedata +1 -1
- package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcodeproj/xcshareddata/xcschemes/{Lottie_iOS.xcscheme → Lottie (iOS).xcscheme } +15 -18
- package/Lottie.xcodeproj/xcshareddata/xcschemes/{Lottie_macOS.xcscheme → Lottie (macOS).xcscheme } +7 -20
- package/Lottie.xcodeproj/xcshareddata/xcschemes/{Lottie_tvOS.xcscheme → Lottie (tvOS).xcscheme } +5 -18
- package/Lottie.xcodeproj/xcuserdata/cal.xcuserdatad/xcschemes/xcschememanagement.plist +37 -0
- package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +24 -0
- package/Lottie.xcworkspace/contents.xcworkspacedata +10 -0
- package/Lottie.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/Lottie.xcworkspace/xcshareddata/swiftpm/Package.resolved +115 -0
- package/Lottie.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/{Lottie.xcodeproj/xcuserdata/brandonwithrow.xcuserdatad → Lottie.xcworkspace/xcuserdata/cal.xcuserdatad}/xcdebugger/Breakpoints_v2.xcbkptlist +2 -2
- package/Lottie.xcworkspace/xcuserdata/cal.xcuserdatad/xcdebugger/Expressions.xcexplist +153 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +39 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +25 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +30 -0
- package/Package.resolved +88 -0
- package/Package.swift +10 -15
- package/README.md +40 -60
- package/Rakefile +121 -0
- package/Sources/Private/CoreAnimation/Animations/CAAnimation+TimingConfiguration.swift +81 -0
- package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +448 -0
- package/Sources/Private/CoreAnimation/Animations/CombinedShapeAnimation.swift +53 -0
- package/Sources/Private/CoreAnimation/Animations/CustomPathAnimation.swift +41 -0
- package/Sources/Private/CoreAnimation/Animations/EllipseAnimation.swift +28 -0
- package/Sources/Private/CoreAnimation/Animations/GradientAnimations.swift +210 -0
- package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +229 -0
- package/Sources/Private/CoreAnimation/Animations/OpacityAnimation.swift +52 -0
- package/Sources/Private/CoreAnimation/Animations/RectangleAnimation.swift +31 -0
- package/Sources/Private/CoreAnimation/Animations/ShapeAnimation.swift +246 -0
- package/Sources/Private/CoreAnimation/Animations/StarAnimation.swift +95 -0
- package/Sources/Private/CoreAnimation/Animations/StrokeAnimation.swift +70 -0
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +205 -0
- package/Sources/Private/CoreAnimation/Animations/VisibilityAnimation.swift +37 -0
- package/Sources/Private/CoreAnimation/CompatibilityTracker.swift +130 -0
- package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +492 -0
- package/Sources/Private/CoreAnimation/Extensions/CALayer+fillBounds.swift +35 -0
- package/Sources/Private/CoreAnimation/Extensions/KeyframeGroup+exactlyOneKeyframe.swift +37 -0
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combinedIfPossible.swift +73 -0
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +83 -0
- package/Sources/Private/CoreAnimation/Layers/BaseAnimationLayer.swift +33 -0
- package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +87 -0
- package/Sources/Private/CoreAnimation/Layers/CALayer+setupLayerHierarchy.swift +131 -0
- package/Sources/Private/CoreAnimation/Layers/GradientRenderLayer.swift +94 -0
- package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +79 -0
- package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +60 -0
- package/Sources/Private/CoreAnimation/Layers/MaskCompositionLayer.swift +138 -0
- package/Sources/Private/CoreAnimation/Layers/PreCompLayer.swift +140 -0
- package/Sources/Private/CoreAnimation/Layers/ShapeItemLayer.swift +302 -0
- package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +319 -0
- package/Sources/Private/CoreAnimation/Layers/SolidLayer.swift +47 -0
- package/Sources/Private/CoreAnimation/Layers/TextLayer.swift +91 -0
- package/Sources/Private/CoreAnimation/Layers/TransformLayer.swift +11 -0
- package/Sources/Private/CoreAnimation/ValueProviderStore.swift +139 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/CompositionLayer.swift +93 -85
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/ImageCompositionLayer.swift +24 -21
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/MaskContainerLayer.swift +75 -54
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/NullCompositionLayer.swift +5 -5
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/PreCompositionLayer.swift +61 -44
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/ShapeCompositionLayer.swift +22 -20
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/SolidCompositionLayer.swift +26 -17
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/TextCompositionLayer.swift +42 -36
- package/{lottie-swift/src/Private/LayerContainers/AnimationContainer.swift → Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift} +200 -140
- package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +47 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/CompositionLayersInitializer.swift +33 -24
- package/{lottie-swift/src/Private/LayerContainers/Utility/TextLayer.swift → Sources/Private/MainThread/LayerContainers/Utility/CoreTextRenderLayer.swift} +120 -106
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/InvertedMatteLayer.swift +25 -24
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerFontProvider.swift +41 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/LayerImageProvider.swift +20 -16
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTextProvider.swift +40 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/LayerTransformNode.swift +82 -67
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Extensions/ItemsExtension.swift +4 -4
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/NodeProperty.swift +29 -21
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.swift +16 -10
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.swift +6 -6
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift +5 -5
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +10 -8
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/ValueContainer.swift +25 -21
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift +23 -17
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.swift +125 -108
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.swift +22 -17
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift +110 -79
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift +22 -16
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift +19 -15
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift +22 -20
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift +22 -23
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +242 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift +30 -24
- package/{lottie-swift/src/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift → Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift} +93 -66
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift +23 -19
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +139 -0
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +170 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/PathNodes/RectNode.swift +95 -58
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/PathNodes/ShapeNode.swift +42 -29
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/PathNodes/StarNode.swift +108 -69
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +155 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift +51 -37
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +54 -42
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +65 -57
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +84 -58
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +138 -119
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Protocols/AnimatorNode.swift +80 -79
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Protocols/PathNode.swift +5 -3
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Protocols/RenderNode.swift +22 -17
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +27 -24
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift +34 -25
- package/Sources/Private/Model/Animation.swift +160 -0
- package/Sources/Private/Model/Assets/Asset.swift +43 -0
- package/{lottie-swift/src → Sources}/Private/Model/Assets/AssetLibrary.swift +40 -13
- package/Sources/Private/Model/Assets/ImageAsset.swift +112 -0
- package/{lottie-swift/src → Sources}/Private/Model/Assets/PrecompAsset.swift +20 -10
- package/Sources/Private/Model/DictionaryInitializable.swift +67 -0
- package/Sources/Private/Model/Extensions/Bundle.swift +34 -0
- package/{lottie-swift/src → Sources}/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +8 -4
- package/Sources/Private/Model/Keyframes/KeyframeData.swift +113 -0
- package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +197 -0
- package/{lottie-swift/src → Sources}/Private/Model/Layers/ImageLayerModel.swift +21 -11
- package/Sources/Private/Model/Layers/LayerModel.swift +228 -0
- package/{lottie-swift/src → Sources}/Private/Model/Layers/PreCompLayerModel.swift +39 -22
- package/{lottie-swift/src → Sources}/Private/Model/Layers/ShapeLayerModel.swift +23 -12
- package/{lottie-swift/src → Sources}/Private/Model/Layers/SolidLayerModel.swift +31 -19
- package/{lottie-swift/src → Sources}/Private/Model/Layers/TextLayerModel.swift +33 -19
- package/Sources/Private/Model/Objects/DashPattern.swift +44 -0
- package/Sources/Private/Model/Objects/Marker.swift +33 -0
- package/Sources/Private/Model/Objects/Mask.swift +80 -0
- package/Sources/Private/Model/Objects/Transform.swift +179 -0
- package/Sources/Private/Model/ShapeItems/Ellipse.swift +75 -0
- package/Sources/Private/Model/ShapeItems/Fill.swift +74 -0
- package/Sources/Private/Model/ShapeItems/GradientFill.swift +124 -0
- package/Sources/Private/Model/ShapeItems/GradientStroke.swift +186 -0
- package/Sources/Private/Model/ShapeItems/Group.swift +48 -0
- package/{lottie-swift/src → Sources}/Private/Model/ShapeItems/Merge.swift +29 -11
- package/Sources/Private/Model/ShapeItems/Rectangle.swift +73 -0
- package/Sources/Private/Model/ShapeItems/Repeater.swift +136 -0
- package/Sources/Private/Model/ShapeItems/Shape.swift +56 -0
- package/Sources/Private/Model/ShapeItems/ShapeItem.swift +163 -0
- package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +136 -0
- package/Sources/Private/Model/ShapeItems/Star.swift +132 -0
- package/Sources/Private/Model/ShapeItems/Stroke.swift +102 -0
- package/Sources/Private/Model/ShapeItems/Trim.swift +78 -0
- package/Sources/Private/Model/Text/Font.swift +61 -0
- package/{lottie-swift/src → Sources}/Private/Model/Text/Glyph.swift +63 -39
- package/Sources/Private/Model/Text/TextAnimator.swift +164 -0
- package/Sources/Private/Model/Text/TextDocument.swift +123 -0
- package/Sources/Private/RootAnimationLayer.swift +51 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Debugging/AnimatorNodeDebugging.swift +7 -7
- package/{lottie-swift/src → Sources}/Private/Utility/Debugging/LayerDebugging.swift +72 -48
- package/Sources/Private/Utility/Debugging/TestHelpers.swift +10 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Extensions/AnimationKeypathExtension.swift +69 -59
- package/Sources/Private/Utility/Extensions/BlendMode+Filter.swift +31 -0
- package/Sources/Private/Utility/Extensions/CGColor+RGB.swift +22 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Extensions/CGFloatExtensions.swift +70 -67
- package/Sources/Private/Utility/Extensions/DataExtension.swift +27 -0
- package/Sources/Private/Utility/Extensions/MathKit.swift +450 -0
- package/Sources/Private/Utility/Extensions/StringExtensions.swift +38 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Helpers/AnimationContext.swift +42 -16
- package/Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift +134 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Interpolatable/KeyframeExtensions.swift +13 -10
- package/Sources/Private/Utility/Interpolatable/KeyframeGroup+Extensions.swift +59 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Primitives/BezierPath.swift +229 -143
- package/Sources/Private/Utility/Primitives/CGPointExtension.swift +35 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Primitives/ColorExtension.swift +46 -14
- package/{lottie-swift/src → Sources}/Private/Utility/Primitives/CompoundBezierPath.swift +58 -49
- package/Sources/Private/Utility/Primitives/CurveVertex.swift +184 -0
- package/Sources/Private/Utility/Primitives/PathElement.swift +75 -0
- package/Sources/Private/Utility/Primitives/UnitBezier.swift +115 -0
- package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +341 -0
- package/Sources/Public/Animation/AnimationPublic.swift +266 -0
- package/Sources/Public/Animation/AnimationView.swift +1335 -0
- package/Sources/Public/Animation/AnimationViewInitializers.swift +109 -0
- package/Sources/Public/AnimationCache/AnimationCacheProvider.swift +22 -0
- package/{lottie-swift/src → Sources}/Public/AnimationCache/LRUAnimationCache.swift +22 -18
- package/Sources/Public/DynamicProperties/AnimationKeypath.swift +49 -0
- package/Sources/Public/DynamicProperties/AnyValueProvider.swift +132 -0
- package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +54 -36
- package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +40 -36
- package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +123 -0
- package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +40 -35
- package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +40 -35
- package/{lottie-swift/src → Sources}/Public/FontProvider/AnimationFontProvider.swift +15 -8
- package/Sources/Public/ImageProvider/AnimationImageProvider.swift +21 -0
- package/Sources/Public/Keyframes/Interpolatable.swift +253 -0
- package/Sources/Public/Keyframes/Keyframe.swift +92 -0
- package/Sources/Public/Logging/LottieLogger.swift +137 -0
- package/Sources/Public/LottieConfiguration.swift +143 -0
- package/{lottie-swift/src → Sources}/Public/Primitives/AnimationTime.swift +1 -1
- package/{lottie-swift/src → Sources}/Public/Primitives/Color.swift +10 -6
- package/{lottie-swift/src → Sources}/Public/Primitives/Vectors.swift +13 -12
- package/Sources/Public/TextProvider/AnimationTextProvider.swift +53 -0
- package/{lottie-swift/src → Sources}/Public/iOS/AnimatedButton.swift +42 -26
- package/{lottie-swift/src → Sources}/Public/iOS/AnimatedControl.swift +100 -91
- package/{lottie-swift/src → Sources}/Public/iOS/AnimatedSwitch.swift +128 -94
- package/{lottie-swift/src → Sources}/Public/iOS/AnimationSubview.swift +3 -3
- package/Sources/Public/iOS/AnimationViewBase.swift +78 -0
- package/{lottie-swift/src → Sources}/Public/iOS/BundleImageProvider.swift +36 -34
- package/{lottie-swift/src → Sources}/Public/iOS/Compatibility/CompatibleAnimationKeypath.swift +4 -0
- package/{lottie-swift/src → Sources}/Public/iOS/Compatibility/CompatibleAnimationView.swift +38 -29
- package/{lottie-swift/src → Sources}/Public/iOS/FilepathImageProvider.swift +24 -21
- package/{lottie-swift/src → Sources}/Public/iOS/UIColorExtension.swift +4 -4
- package/{lottie-swift/src/Public/MacOS/AnimationSubview.swift → Sources/Public/macOS/AnimationSubview.macOS.swift} +4 -5
- package/{lottie-swift/src/Public/MacOS/LottieView.swift → Sources/Public/macOS/AnimationViewBase.macOS.swift} +58 -50
- package/{lottie-swift/src/Public/MacOS/BundleImageProvider.swift → Sources/Public/macOS/BundleImageProvider.macOS.swift} +31 -32
- package/Sources/Public/macOS/FilepathImageProvider.macOS.swift +67 -0
- package/Tests/AnimationKeypathTests.swift +94 -0
- package/Tests/AutomaticEngineTests.swift +57 -0
- package/Tests/BundleTests.swift +25 -0
- package/Tests/DataURLTests.swift +64 -0
- package/Tests/ParsingTests.swift +43 -0
- package/Tests/PerformanceTests.swift +215 -0
- package/Tests/SnapshotConfiguration.swift +153 -0
- package/Tests/SnapshotTests.swift +265 -0
- package/Tests/Utils/Bundle+Module.swift +30 -0
- package/Tests/Utils/HardcodedFontProvider.swift +19 -0
- package/Tests/Utils/HardcodedImageProvider.swift +23 -0
- package/Tests/Utils/Snapshotting+presentationLayer.swift +47 -0
- package/Tests/ValueProvidersTests.swift +27 -0
- package/lottie-ios.podspec +11 -12
- package/package.json +1 -1
- package/script/test-carthage/Cartfile +1 -0
- package/script/test-carthage/Cartfile.resolved +1 -0
- package/script/test-carthage/CarthageTest/AppDelegate.swift +26 -0
- package/script/test-carthage/CarthageTest/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- package/script/test-carthage/CarthageTest/Assets.xcassets/AppIcon.appiconset/Contents.json +98 -0
- package/script/test-carthage/CarthageTest/Assets.xcassets/Contents.json +6 -0
- package/script/test-carthage/CarthageTest/Base.lproj/LaunchScreen.storyboard +25 -0
- package/script/test-carthage/CarthageTest/Base.lproj/Main.storyboard +24 -0
- package/script/test-carthage/CarthageTest/Info.plist +66 -0
- package/script/test-carthage/CarthageTest/SceneDelegate.swift +10 -0
- package/script/test-carthage/CarthageTest/ViewController.swift +15 -0
- package/script/test-carthage/CarthageTest-macOS/AppDelegate.swift +7 -0
- package/script/test-carthage/CarthageTest-macOS/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- package/script/test-carthage/CarthageTest-macOS/Assets.xcassets/AppIcon.appiconset/Contents.json +58 -0
- package/script/test-carthage/CarthageTest-macOS/Assets.xcassets/Contents.json +6 -0
- package/script/test-carthage/CarthageTest-macOS/Base.lproj/Main.storyboard +717 -0
- package/script/test-carthage/CarthageTest-macOS/CarthageTest_macOS.entitlements +10 -0
- package/script/test-carthage/CarthageTest-macOS/ViewController.swift +15 -0
- package/script/test-carthage/CarthageTest.xcodeproj/project.pbxproj +532 -0
- package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/script/test-carthage/CarthageTest.xcodeproj/xcuserdata/cal.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/script/test-carthage/CarthageTest.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +19 -0
- package/script/test-carthage/Mintfile +1 -0
- package/script/test-spm/LottieSPM.xcworkspace/contents.xcworkspacedata +7 -0
- package/script/test-spm/LottieSPM.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/script/test-spm/Mintfile +1 -0
- package/.swiftpm/xcode/package.xcworkspace/xcuserdata/brandonwithrow.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie/Info.plist +0 -22
- package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/brandonwithrow.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcodeproj/xcuserdata/brandonwithrow.xcuserdatad/xcschemes/LottieLibraryMacOS.xcscheme +0 -80
- package/Lottie.xcodeproj/xcuserdata/brandonwithrow.xcuserdatad/xcschemes/xcschememanagement.plist +0 -57
- package/lottie-swift/Assets/.gitkeep +0 -0
- package/lottie-swift/src/Private/LayerContainers/Utility/LayerFontProvider.swift +0 -37
- package/lottie-swift/src/Private/LayerContainers/Utility/LayerTextProvider.swift +0 -36
- package/lottie-swift/src/Private/Model/Animation.swift +0 -107
- package/lottie-swift/src/Private/Model/Assets/Asset.swift +0 -27
- package/lottie-swift/src/Private/Model/Assets/ImageAsset.swift +0 -48
- package/lottie-swift/src/Private/Model/Keyframes/Keyframe.swift +0 -128
- package/lottie-swift/src/Private/Model/Keyframes/KeyframeGroup.swift +0 -108
- package/lottie-swift/src/Private/Model/Layers/LayerModel.swift +0 -150
- package/lottie-swift/src/Private/Model/Objects/DashPattern.swift +0 -24
- package/lottie-swift/src/Private/Model/Objects/Marker.swift +0 -23
- package/lottie-swift/src/Private/Model/Objects/Mask.swift +0 -48
- package/lottie-swift/src/Private/Model/Objects/Transform.swift +0 -105
- package/lottie-swift/src/Private/Model/ShapeItems/Ellipse.swift +0 -50
- package/lottie-swift/src/Private/Model/ShapeItems/FillI.swift +0 -49
- package/lottie-swift/src/Private/Model/ShapeItems/GradientFill.swift +0 -86
- package/lottie-swift/src/Private/Model/ShapeItems/GradientStroke.swift +0 -125
- package/lottie-swift/src/Private/Model/ShapeItems/Group.swift +0 -32
- package/lottie-swift/src/Private/Model/ShapeItems/Rectangle.swift +0 -50
- package/lottie-swift/src/Private/Model/ShapeItems/Repeater.swift +0 -80
- package/lottie-swift/src/Private/Model/ShapeItems/Shape.swift +0 -37
- package/lottie-swift/src/Private/Model/ShapeItems/ShapeItem.swift +0 -95
- package/lottie-swift/src/Private/Model/ShapeItems/ShapeTransform.swift +0 -68
- package/lottie-swift/src/Private/Model/ShapeItems/Star.swift +0 -86
- package/lottie-swift/src/Private/Model/ShapeItems/Stroke.swift +0 -67
- package/lottie-swift/src/Private/Model/ShapeItems/Trim.swift +0 -53
- package/lottie-swift/src/Private/Model/Text/Font.swift +0 -35
- package/lottie-swift/src/Private/Model/Text/TextAnimator.swift +0 -99
- package/lottie-swift/src/Private/Model/Text/TextDocument.swift +0 -70
- package/lottie-swift/src/Private/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +0 -109
- package/lottie-swift/src/Private/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +0 -132
- package/lottie-swift/src/Private/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +0 -141
- package/lottie-swift/src/Private/Utility/Extensions/MathKit.swift +0 -539
- package/lottie-swift/src/Private/Utility/Extensions/StringExtensions.swift +0 -32
- package/lottie-swift/src/Private/Utility/Interpolatable/Interpolatable.swift +0 -18
- package/lottie-swift/src/Private/Utility/Interpolatable/InterpolatableExtensions.swift +0 -170
- package/lottie-swift/src/Private/Utility/Primitives/CurveVertex.swift +0 -177
- package/lottie-swift/src/Private/Utility/Primitives/PathElement.swift +0 -68
- package/lottie-swift/src/Private/Utility/Primitives/VectorsExtensions.swift +0 -218
- package/lottie-swift/src/Public/Animation/AnimationPublic.swift +0 -196
- package/lottie-swift/src/Public/Animation/AnimationView.swift +0 -1006
- package/lottie-swift/src/Public/Animation/AnimationViewInitializers.swift +0 -83
- package/lottie-swift/src/Public/AnimationCache/AnimationCacheProvider.swift +0 -24
- package/lottie-swift/src/Public/DynamicProperties/AnimationKeypath.swift +0 -46
- package/lottie-swift/src/Public/DynamicProperties/AnyValueProvider.swift +0 -29
- package/lottie-swift/src/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +0 -114
- package/lottie-swift/src/Public/ImageProvider/AnimationImageProvider.swift +0 -23
- package/lottie-swift/src/Public/MacOS/FilepathImageProvider.swift +0 -67
- package/lottie-swift/src/Public/TextProvider/AnimationTextProvider.swift +0 -39
- package/lottie-swift/src/Public/iOS/LottieView.swift +0 -62
- package/lottie-swift-testing.podspec +0 -32
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Created by Cal Stephens on 1/6/22.
|
|
2
|
+
// Copyright © 2022 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import QuartzCore
|
|
5
|
+
|
|
6
|
+
// MARK: - MaskCompositionLayer
|
|
7
|
+
|
|
8
|
+
/// The CALayer type responsible for rendering the `Mask` of a `BaseCompositionLayer`
|
|
9
|
+
final class MaskCompositionLayer: CALayer {
|
|
10
|
+
|
|
11
|
+
// MARK: Lifecycle
|
|
12
|
+
|
|
13
|
+
init(masks: [Mask]) {
|
|
14
|
+
maskLayers = masks.map(MaskLayer.init(mask:))
|
|
15
|
+
super.init()
|
|
16
|
+
|
|
17
|
+
var containerLayer = BaseAnimationLayer()
|
|
18
|
+
var firstObject = true
|
|
19
|
+
for maskLayer in maskLayers {
|
|
20
|
+
if maskLayer.maskModel.mode.usableMode == .none {
|
|
21
|
+
continue
|
|
22
|
+
} else if maskLayer.maskModel.mode.usableMode == .add || firstObject {
|
|
23
|
+
firstObject = false
|
|
24
|
+
containerLayer.addSublayer(maskLayer)
|
|
25
|
+
} else {
|
|
26
|
+
containerLayer.mask = maskLayer
|
|
27
|
+
let newContainer = BaseAnimationLayer()
|
|
28
|
+
newContainer.addSublayer(containerLayer)
|
|
29
|
+
containerLayer = newContainer
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
addSublayer(containerLayer)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
required init?(coder _: NSCoder) {
|
|
37
|
+
fatalError("init(coder:) has not been implemented")
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// Called by CoreAnimation to create a shadow copy of this layer
|
|
41
|
+
/// More details: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init
|
|
42
|
+
override init(layer: Any) {
|
|
43
|
+
guard let typedLayer = layer as? Self else {
|
|
44
|
+
fatalError("\(Self.self).init(layer:) incorrectly called with \(type(of: layer))")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
maskLayers = typedLayer.maskLayers
|
|
48
|
+
super.init(layer: typedLayer)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// MARK: Internal
|
|
52
|
+
|
|
53
|
+
override func layoutSublayers() {
|
|
54
|
+
super.layoutSublayers()
|
|
55
|
+
|
|
56
|
+
for sublayer in sublayers ?? [] {
|
|
57
|
+
sublayer.fillBoundsOfSuperlayer()
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// MARK: Private
|
|
62
|
+
|
|
63
|
+
private let maskLayers: [MaskLayer]
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// MARK: AnimationLayer
|
|
68
|
+
|
|
69
|
+
extension MaskCompositionLayer: AnimationLayer {
|
|
70
|
+
func setupAnimations(context: LayerAnimationContext) throws {
|
|
71
|
+
for maskLayer in maskLayers {
|
|
72
|
+
try maskLayer.setupAnimations(context: context)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// MARK: - MaskLayer
|
|
78
|
+
|
|
79
|
+
extension MaskCompositionLayer {
|
|
80
|
+
final class MaskLayer: CAShapeLayer {
|
|
81
|
+
|
|
82
|
+
// MARK: Lifecycle
|
|
83
|
+
|
|
84
|
+
init(mask: Mask) {
|
|
85
|
+
maskModel = mask
|
|
86
|
+
super.init()
|
|
87
|
+
|
|
88
|
+
fillRule = .evenOdd
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
required init?(coder _: NSCoder) {
|
|
92
|
+
fatalError("init(coder:) has not been implemented")
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// Called by CoreAnimation to create a shadow copy of this layer
|
|
96
|
+
/// More details: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init
|
|
97
|
+
override init(layer: Any) {
|
|
98
|
+
guard let typedLayer = layer as? Self else {
|
|
99
|
+
fatalError("\(Self.self).init(layer:) incorrectly called with \(type(of: layer))")
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
maskModel = typedLayer.maskModel
|
|
103
|
+
super.init(layer: typedLayer)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// MARK: Internal
|
|
107
|
+
|
|
108
|
+
let maskModel: Mask
|
|
109
|
+
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// MARK: - MaskCompositionLayer.MaskLayer + AnimationLayer
|
|
114
|
+
|
|
115
|
+
extension MaskCompositionLayer.MaskLayer: AnimationLayer {
|
|
116
|
+
func setupAnimations(context: LayerAnimationContext) throws {
|
|
117
|
+
let shouldInvertMask = (maskModel.mode.usableMode == .subtract && !maskModel.inverted)
|
|
118
|
+
|| (maskModel.mode.usableMode == .add && maskModel.inverted)
|
|
119
|
+
|
|
120
|
+
try addAnimations(
|
|
121
|
+
for: maskModel.shape,
|
|
122
|
+
context: context,
|
|
123
|
+
transformPath: { maskPath in
|
|
124
|
+
// If the mask is using `MaskMode.subtract` or has `inverted: true`,
|
|
125
|
+
// we have to invert the area filled by the path. We can do that by
|
|
126
|
+
// drawing a rectangle, and then adding a path (which is subtracted
|
|
127
|
+
// from the rectangle based on the .evenOdd fill mode).
|
|
128
|
+
if shouldInvertMask {
|
|
129
|
+
let path = CGMutablePath()
|
|
130
|
+
path.addRect(.veryLargeRect)
|
|
131
|
+
path.addPath(maskPath)
|
|
132
|
+
return path
|
|
133
|
+
} else {
|
|
134
|
+
return maskPath
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Created by Cal Stephens on 12/14/21.
|
|
2
|
+
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import QuartzCore
|
|
5
|
+
|
|
6
|
+
// MARK: - PreCompLayer
|
|
7
|
+
|
|
8
|
+
/// The `CALayer` type responsible for rendering `PreCompLayerModel`s
|
|
9
|
+
final class PreCompLayer: BaseCompositionLayer {
|
|
10
|
+
|
|
11
|
+
// MARK: Lifecycle
|
|
12
|
+
|
|
13
|
+
init(preCompLayer: PreCompLayerModel) {
|
|
14
|
+
self.preCompLayer = preCompLayer
|
|
15
|
+
super.init(layerModel: preCompLayer)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
required init?(coder _: NSCoder) {
|
|
19
|
+
fatalError("init(coder:) has not been implemented")
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// Called by CoreAnimation to create a shadow copy of this layer
|
|
23
|
+
/// More details: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init
|
|
24
|
+
override init(layer: Any) {
|
|
25
|
+
guard let typedLayer = layer as? Self else {
|
|
26
|
+
fatalError("\(Self.self).init(layer:) incorrectly called with \(type(of: layer))")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
preCompLayer = typedLayer.preCompLayer
|
|
30
|
+
timeRemappingInterpolator = typedLayer.timeRemappingInterpolator
|
|
31
|
+
super.init(layer: typedLayer)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// MARK: Internal
|
|
35
|
+
|
|
36
|
+
/// Post-init setup for `PreCompLayer`s.
|
|
37
|
+
/// Should always be called after `PreCompLayer.init(preCompLayer:)`.
|
|
38
|
+
///
|
|
39
|
+
/// This is a workaround for a hard-to-reproduce crash that was
|
|
40
|
+
/// triggered when `PreCompLayer.init` was called reentantly. We didn't
|
|
41
|
+
/// have any consistent repro steps for this crash (it happened 100% of
|
|
42
|
+
/// the time for some testers, and 0% of the time for other testers),
|
|
43
|
+
/// but moving this code out of `PreCompLayer.init` does seem to fix it.
|
|
44
|
+
///
|
|
45
|
+
/// The stack trace looked like:
|
|
46
|
+
/// - `_os_unfair_lock_recursive_abort`
|
|
47
|
+
/// - `-[CALayerAccessibility__UIKit__QuartzCore dealloc]`
|
|
48
|
+
/// - `PreCompLayer.__allocating_init(preCompLayer:context:)` <- reentrant init call
|
|
49
|
+
/// - ...
|
|
50
|
+
/// - `CALayer.setupLayerHierarchy(for:context:)`
|
|
51
|
+
/// - `PreCompLayer.init(preCompLayer:context:)`
|
|
52
|
+
///
|
|
53
|
+
func setup(context: LayerContext) throws {
|
|
54
|
+
if let timeRemappingKeyframes = preCompLayer.timeRemapping {
|
|
55
|
+
timeRemappingInterpolator = try .timeRemapping(keyframes: timeRemappingKeyframes, context: context)
|
|
56
|
+
} else {
|
|
57
|
+
timeRemappingInterpolator = nil
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
try setupLayerHierarchy(
|
|
61
|
+
for: context.animation.assetLibrary?.precompAssets[preCompLayer.referenceID]?.layers ?? [],
|
|
62
|
+
context: context)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
override func setupAnimations(context: LayerAnimationContext) throws {
|
|
66
|
+
var context = context
|
|
67
|
+
context = context.addingKeypathComponent(preCompLayer.name)
|
|
68
|
+
try setupLayerAnimations(context: context)
|
|
69
|
+
|
|
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`
|
|
72
|
+
let contextForChildren = context.withTimeRemapping { [preCompLayer, timeRemappingInterpolator] layerLocalFrame in
|
|
73
|
+
if let timeRemappingInterpolator = timeRemappingInterpolator {
|
|
74
|
+
return timeRemappingInterpolator.value(frame: layerLocalFrame) as? AnimationFrameTime ?? layerLocalFrame
|
|
75
|
+
} else {
|
|
76
|
+
return layerLocalFrame + AnimationFrameTime(preCompLayer.startTime)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try setupChildAnimations(context: contextForChildren)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// MARK: Private
|
|
84
|
+
|
|
85
|
+
private let preCompLayer: PreCompLayerModel
|
|
86
|
+
private var timeRemappingInterpolator: KeyframeInterpolator<AnimationFrameTime>?
|
|
87
|
+
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// MARK: CustomLayoutLayer
|
|
91
|
+
|
|
92
|
+
extension PreCompLayer: CustomLayoutLayer {
|
|
93
|
+
func layout(superlayerBounds: CGRect) {
|
|
94
|
+
anchorPoint = .zero
|
|
95
|
+
|
|
96
|
+
// Pre-comp layers use a size specified in the layer model,
|
|
97
|
+
// and clip the composition to that bounds
|
|
98
|
+
bounds = CGRect(
|
|
99
|
+
x: superlayerBounds.origin.x,
|
|
100
|
+
y: superlayerBounds.origin.y,
|
|
101
|
+
width: CGFloat(preCompLayer.width),
|
|
102
|
+
height: CGFloat(preCompLayer.height))
|
|
103
|
+
|
|
104
|
+
masksToBounds = true
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
extension KeyframeInterpolator where ValueType == AnimationFrameTime {
|
|
109
|
+
/// A `KeyframeInterpolator` for the given `timeRemapping` keyframes
|
|
110
|
+
static func timeRemapping(
|
|
111
|
+
keyframes timeRemappingKeyframes: KeyframeGroup<Vector1D>,
|
|
112
|
+
context: LayerContext)
|
|
113
|
+
throws
|
|
114
|
+
-> KeyframeInterpolator<AnimationFrameTime>
|
|
115
|
+
{
|
|
116
|
+
try context.logCompatibilityIssue("""
|
|
117
|
+
The Core Animation rendering engine partially supports time remapping keyframes,
|
|
118
|
+
but this is somewhat experimental and has some known issues. Since it doesn't work
|
|
119
|
+
in all cases, we have to fall back to using the main thread engine when using
|
|
120
|
+
`RenderingEngineOption.automatic`.
|
|
121
|
+
""")
|
|
122
|
+
|
|
123
|
+
// `timeRemapping` is a mapping from the animation's global time to the layer's local time.
|
|
124
|
+
// In the Core Animation engine, we need to perform the opposite calculation -- convert
|
|
125
|
+
// the layer's local time into the animation's global time. We can get this by inverting
|
|
126
|
+
// the time remapping, swapping the x axis (global time) and the y axis (local time).
|
|
127
|
+
let localTimeToGlobalTimeMapping = timeRemappingKeyframes.keyframes.map { keyframe in
|
|
128
|
+
Keyframe(
|
|
129
|
+
value: keyframe.time,
|
|
130
|
+
time: keyframe.value.cgFloatValue * CGFloat(context.animation.framerate),
|
|
131
|
+
isHold: keyframe.isHold,
|
|
132
|
+
inTangent: keyframe.inTangent,
|
|
133
|
+
outTangent: keyframe.outTangent,
|
|
134
|
+
spatialInTangent: keyframe.spatialInTangent,
|
|
135
|
+
spatialOutTangent: keyframe.spatialOutTangent)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return KeyframeInterpolator(keyframes: .init(localTimeToGlobalTimeMapping))
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
// Created by Cal Stephens on 12/13/21.
|
|
2
|
+
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import QuartzCore
|
|
5
|
+
|
|
6
|
+
// MARK: - ShapeItemLayer
|
|
7
|
+
|
|
8
|
+
/// A CALayer type that renders an array of `[ShapeItem]`s,
|
|
9
|
+
/// from a `Group` in a `ShapeLayerModel`.
|
|
10
|
+
final class ShapeItemLayer: BaseAnimationLayer {
|
|
11
|
+
|
|
12
|
+
// MARK: Lifecycle
|
|
13
|
+
|
|
14
|
+
/// Initializes a `ShapeItemLayer` that renders a `Group` from a `ShapeLayerModel`
|
|
15
|
+
/// - Parameters:
|
|
16
|
+
/// - shape: The `ShapeItem` in this group that renders a `GGPath`
|
|
17
|
+
/// - otherItems: Other items in this group that affect the appearance of the shape
|
|
18
|
+
init(shape: Item, otherItems: [Item], context: LayerContext) throws {
|
|
19
|
+
self.shape = shape
|
|
20
|
+
self.otherItems = otherItems
|
|
21
|
+
|
|
22
|
+
try context.compatibilityAssert(
|
|
23
|
+
shape.item.drawsCGPath,
|
|
24
|
+
"`ShapeItemLayer` must contain exactly one `ShapeItem` that draws a `GPPath`")
|
|
25
|
+
|
|
26
|
+
try context.compatibilityAssert(
|
|
27
|
+
!otherItems.contains(where: { $0.item.drawsCGPath }),
|
|
28
|
+
"`ShapeItemLayer` must contain exactly one `ShapeItem` that draws a `GPPath`")
|
|
29
|
+
|
|
30
|
+
super.init()
|
|
31
|
+
|
|
32
|
+
setupLayerHierarchy()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
required init?(coder _: NSCoder) {
|
|
36
|
+
fatalError("init(coder:) has not been implemented")
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Called by CoreAnimation to create a shadow copy of this layer
|
|
40
|
+
/// More details: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init
|
|
41
|
+
override init(layer: Any) {
|
|
42
|
+
guard let typedLayer = layer as? Self else {
|
|
43
|
+
fatalError("\(Self.self).init(layer:) incorrectly called with \(type(of: layer))")
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
shape = typedLayer.shape
|
|
47
|
+
otherItems = typedLayer.otherItems
|
|
48
|
+
super.init(layer: typedLayer)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// MARK: Internal
|
|
52
|
+
|
|
53
|
+
/// An item that can be displayed by this layer
|
|
54
|
+
struct Item {
|
|
55
|
+
/// A `ShapeItem` that should be rendered by this layer
|
|
56
|
+
let item: ShapeItem
|
|
57
|
+
|
|
58
|
+
/// The group that contains this `ShapeItem`, if applicable
|
|
59
|
+
let parentGroup: Group?
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
override func setupAnimations(context: LayerAnimationContext) throws {
|
|
63
|
+
try super.setupAnimations(context: context)
|
|
64
|
+
|
|
65
|
+
guard let sublayerConfiguration = sublayerConfiguration else { return }
|
|
66
|
+
|
|
67
|
+
switch sublayerConfiguration.fill {
|
|
68
|
+
case .solidFill(let shapeLayer):
|
|
69
|
+
try setupSolidFillAnimations(shapeLayer: shapeLayer, context: context)
|
|
70
|
+
|
|
71
|
+
case .gradientFill(let gradientLayers):
|
|
72
|
+
try setupGradientFillAnimations(layers: gradientLayers, context: context)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if let gradientStrokeConfiguration = sublayerConfiguration.gradientStroke {
|
|
76
|
+
try setupGradientStrokeAnimations(layers: gradientStrokeConfiguration, context: context)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// MARK: Private
|
|
81
|
+
|
|
82
|
+
private struct GradientLayers {
|
|
83
|
+
/// The `CALayer` that renders the RGB components of the gradient
|
|
84
|
+
let gradientColorLayer: GradientRenderLayer
|
|
85
|
+
/// The `CALayer` that renders the alpha components of the gradient,
|
|
86
|
+
/// masking the `gradientColorLayer`
|
|
87
|
+
let gradientAlphaLayer: GradientRenderLayer?
|
|
88
|
+
/// The `CAShapeLayer` that clips the gradient layers to the expected shape
|
|
89
|
+
let shapeMaskLayer: CAShapeLayer
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// The configuration of this layer's `fill` sublayers
|
|
93
|
+
private enum FillLayerConfiguration {
|
|
94
|
+
/// This layer displays a single `CAShapeLayer`
|
|
95
|
+
case solidFill(CAShapeLayer)
|
|
96
|
+
|
|
97
|
+
/// This layer displays a `GradientRenderLayer` masked by a `CAShapeLayer`.
|
|
98
|
+
case gradientFill(GradientLayers)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/// The `ShapeItem` in this group that renders a `GGPath`
|
|
102
|
+
private let shape: Item
|
|
103
|
+
|
|
104
|
+
/// Other items in this group that affect the appearance of the shape
|
|
105
|
+
private let otherItems: [Item]
|
|
106
|
+
|
|
107
|
+
/// The current configuration of this layer's sublayer(s)
|
|
108
|
+
private var sublayerConfiguration: (fill: FillLayerConfiguration, gradientStroke: GradientLayers?)?
|
|
109
|
+
|
|
110
|
+
private func setupLayerHierarchy() {
|
|
111
|
+
// We have to build a different layer hierarchy depending on if
|
|
112
|
+
// we're rendering a gradient (a `CAGradientLayer` masked by a `CAShapeLayer`)
|
|
113
|
+
// or a solid shape (a simple `CAShapeLayer`).
|
|
114
|
+
let fillLayerConfiguration: FillLayerConfiguration
|
|
115
|
+
if let gradientFill = otherItems.first(GradientFill.self) {
|
|
116
|
+
fillLayerConfiguration = setupGradientFillLayerHierarchy(for: gradientFill)
|
|
117
|
+
} else {
|
|
118
|
+
fillLayerConfiguration = setupSolidFillLayerHierarchy()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let gradientStrokeConfiguration: GradientLayers?
|
|
122
|
+
if let gradientStroke = otherItems.first(GradientStroke.self) {
|
|
123
|
+
gradientStrokeConfiguration = setupGradientStrokeLayerHierarchy(for: gradientStroke)
|
|
124
|
+
} else {
|
|
125
|
+
gradientStrokeConfiguration = nil
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
sublayerConfiguration = (fillLayerConfiguration, gradientStrokeConfiguration)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private func setupSolidFillLayerHierarchy() -> FillLayerConfiguration {
|
|
132
|
+
let shapeLayer = CAShapeLayer()
|
|
133
|
+
addSublayer(shapeLayer)
|
|
134
|
+
|
|
135
|
+
// `CAShapeLayer.fillColor` defaults to black, so we have to
|
|
136
|
+
// nil out the background color if there isn't an expected fill color
|
|
137
|
+
if !otherItems.contains(where: { $0.item is Fill }) {
|
|
138
|
+
shapeLayer.fillColor = nil
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return .solidFill(shapeLayer)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private func setupGradientFillLayerHierarchy(
|
|
145
|
+
for gradientFill: GradientFill)
|
|
146
|
+
-> FillLayerConfiguration
|
|
147
|
+
{
|
|
148
|
+
let pathMask = CAShapeLayer()
|
|
149
|
+
pathMask.fillColor = .rgb(0, 0, 0)
|
|
150
|
+
mask = pathMask
|
|
151
|
+
|
|
152
|
+
let rgbGradientLayer = GradientRenderLayer()
|
|
153
|
+
addSublayer(rgbGradientLayer)
|
|
154
|
+
|
|
155
|
+
let alphaGradientLayer: GradientRenderLayer?
|
|
156
|
+
if gradientFill.hasAlphaComponent {
|
|
157
|
+
alphaGradientLayer = GradientRenderLayer()
|
|
158
|
+
rgbGradientLayer.mask = alphaGradientLayer
|
|
159
|
+
} else {
|
|
160
|
+
alphaGradientLayer = nil
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return .gradientFill(GradientLayers(
|
|
164
|
+
gradientColorLayer: rgbGradientLayer,
|
|
165
|
+
gradientAlphaLayer: alphaGradientLayer,
|
|
166
|
+
shapeMaskLayer: pathMask))
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private func setupGradientStrokeLayerHierarchy(
|
|
170
|
+
for gradientStroke: GradientStroke)
|
|
171
|
+
-> GradientLayers
|
|
172
|
+
{
|
|
173
|
+
let container = BaseAnimationLayer()
|
|
174
|
+
|
|
175
|
+
let pathMask = CAShapeLayer()
|
|
176
|
+
pathMask.fillColor = nil
|
|
177
|
+
pathMask.strokeColor = .rgb(0, 0, 0)
|
|
178
|
+
container.mask = pathMask
|
|
179
|
+
|
|
180
|
+
let rgbGradientLayer = GradientRenderLayer()
|
|
181
|
+
container.addSublayer(rgbGradientLayer)
|
|
182
|
+
addSublayer(container)
|
|
183
|
+
|
|
184
|
+
let alphaGradientLayer: GradientRenderLayer?
|
|
185
|
+
if gradientStroke.hasAlphaComponent {
|
|
186
|
+
alphaGradientLayer = GradientRenderLayer()
|
|
187
|
+
rgbGradientLayer.mask = alphaGradientLayer
|
|
188
|
+
} else {
|
|
189
|
+
alphaGradientLayer = nil
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return GradientLayers(
|
|
193
|
+
gradientColorLayer: rgbGradientLayer,
|
|
194
|
+
gradientAlphaLayer: alphaGradientLayer,
|
|
195
|
+
shapeMaskLayer: pathMask)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private func setupSolidFillAnimations(
|
|
199
|
+
shapeLayer: CAShapeLayer,
|
|
200
|
+
context: LayerAnimationContext)
|
|
201
|
+
throws
|
|
202
|
+
{
|
|
203
|
+
var trimPathMultiplier: PathMultiplier? = nil
|
|
204
|
+
if let (trim, context) = otherItems.first(Trim.self, context: context) {
|
|
205
|
+
trimPathMultiplier = try shapeLayer.addAnimations(for: trim, context: context)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
try shapeLayer.addAnimations(for: shape.item, context: context.for(shape), pathMultiplier: trimPathMultiplier ?? 1)
|
|
209
|
+
|
|
210
|
+
if let (fill, context) = otherItems.first(Fill.self, context: context) {
|
|
211
|
+
try shapeLayer.addAnimations(for: fill, context: context)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if let (stroke, context) = otherItems.first(Stroke.self, context: context) {
|
|
215
|
+
try shapeLayer.addStrokeAnimations(for: stroke, context: context)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private func setupGradientFillAnimations(
|
|
220
|
+
layers: GradientLayers,
|
|
221
|
+
context: LayerAnimationContext)
|
|
222
|
+
throws
|
|
223
|
+
{
|
|
224
|
+
try layers.shapeMaskLayer.addAnimations(
|
|
225
|
+
for: shape.item,
|
|
226
|
+
context: context.for(shape),
|
|
227
|
+
pathMultiplier: 1)
|
|
228
|
+
|
|
229
|
+
if let (gradientFill, context) = otherItems.first(GradientFill.self, context: context) {
|
|
230
|
+
try layers.gradientColorLayer.addGradientAnimations(for: gradientFill, type: .rgb, context: context)
|
|
231
|
+
try layers.gradientAlphaLayer?.addGradientAnimations(for: gradientFill, type: .alpha, context: context)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
private func setupGradientStrokeAnimations(
|
|
236
|
+
layers: GradientLayers,
|
|
237
|
+
context: LayerAnimationContext)
|
|
238
|
+
throws
|
|
239
|
+
{
|
|
240
|
+
var trimPathMultiplier: PathMultiplier? = nil
|
|
241
|
+
if let (trim, context) = otherItems.first(Trim.self, context: context) {
|
|
242
|
+
trimPathMultiplier = try layers.shapeMaskLayer.addAnimations(for: trim, context: context)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
try layers.shapeMaskLayer.addAnimations(
|
|
246
|
+
for: shape.item,
|
|
247
|
+
context: context.for(shape),
|
|
248
|
+
pathMultiplier: trimPathMultiplier ?? 1)
|
|
249
|
+
|
|
250
|
+
if let (gradientStroke, context) = otherItems.first(GradientStroke.self, context: context) {
|
|
251
|
+
try layers.gradientColorLayer.addGradientAnimations(for: gradientStroke, type: .rgb, context: context)
|
|
252
|
+
try layers.gradientAlphaLayer?.addGradientAnimations(for: gradientStroke, type: .alpha, context: context)
|
|
253
|
+
|
|
254
|
+
try layers.shapeMaskLayer.addStrokeAnimations(for: gradientStroke, context: context)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// MARK: - [ShapeItem] helpers
|
|
261
|
+
|
|
262
|
+
extension Array where Element == ShapeItemLayer.Item {
|
|
263
|
+
/// The first `ShapeItem` in this array of the given type
|
|
264
|
+
func first<ItemType: ShapeItem>(
|
|
265
|
+
_: ItemType.Type, context: LayerAnimationContext)
|
|
266
|
+
-> (item: ItemType, context: LayerAnimationContext)?
|
|
267
|
+
{
|
|
268
|
+
for item in self {
|
|
269
|
+
if let match = item.item as? ItemType {
|
|
270
|
+
return (match, context.for(item))
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return nil
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/// The first `ShapeItem` in this array of the given type
|
|
278
|
+
func first<ItemType: ShapeItem>(_: ItemType.Type) -> ItemType? {
|
|
279
|
+
for item in self {
|
|
280
|
+
if let match = item.item as? ItemType {
|
|
281
|
+
return match
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return nil
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
extension LayerAnimationContext {
|
|
290
|
+
/// An updated `LayerAnimationContext` with the`AnimationKeypath`
|
|
291
|
+
/// that refers to this specific `ShapeItem`.
|
|
292
|
+
func `for`(_ item: ShapeItemLayer.Item) -> LayerAnimationContext {
|
|
293
|
+
var context = self
|
|
294
|
+
|
|
295
|
+
if let group = item.parentGroup {
|
|
296
|
+
context.currentKeypath.keys.append(group.name)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
context.currentKeypath.keys.append(item.item.name)
|
|
300
|
+
return context
|
|
301
|
+
}
|
|
302
|
+
}
|