lottie-ios 4.2.0 → 4.3.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/workflows/main.yml +117 -11
- package/Gemfile +1 -0
- package/Gemfile.lock +5 -0
- package/Lottie.xcodeproj/project.pbxproj +1546 -194
- package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (iOS).xcscheme +1 -1
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (macOS).xcscheme +1 -1
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (tvOS).xcscheme +4 -5
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (visionOS).xcscheme +66 -0
- package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +18 -0
- package/Lottie.xcworkspace/xcshareddata/swiftpm/Package.resolved +2 -11
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +374 -116
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +16 -5
- package/Package.resolved +2 -2
- package/Package.swift +16 -9
- package/README.md +2 -2
- package/Rakefile +122 -25
- package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +3 -4
- package/Sources/Private/CoreAnimation/Animations/DropShadowAnimation.swift +160 -0
- package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +83 -10
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +85 -79
- package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +11 -7
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +67 -6
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +21 -2
- package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +9 -0
- package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +1 -0
- package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +1 -1
- package/Sources/Private/CoreAnimation/Layers/RepeaterLayer.swift +2 -0
- package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +11 -13
- package/Sources/Private/CoreAnimation/Layers/SolidLayer.swift +20 -5
- package/Sources/Private/CoreAnimation/Layers/TextLayer.swift +13 -3
- package/Sources/Private/CoreAnimation/ValueProviderStore.swift +6 -4
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Collection+Diff.swift +263 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Diffable.swift +18 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/DiffableSection.swift +16 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/IndexChangeset.swift +187 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/SectionedChangeset.swift +32 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Logging/EpoxyLogger.swift +99 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/CallbackContextEpoxyModeled.swift +8 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelArrayBuilder.swift +48 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelProperty.swift +158 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelStorage.swift +88 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModeled.swift +54 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/AnyEpoxyModelProperty.swift +29 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/ClassReference.swift +39 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/AnimatedProviding.swift +10 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DataIDProviding.swift +57 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidDisplayProviding.swift +41 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidEndDisplayingProviding.swift +41 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidSelectProviding.swift +36 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ErasedContentProviding.swift +49 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/MakeViewProviding.swift +60 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetBehaviorsProviding.swift +38 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetContentProviding.swift +38 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/StyleIDProviding.swift +37 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/TraitCollectionProviding.swift +14 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewDifferentiatorProviding.swift +34 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewProviding.swift +13 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/WillDisplayProviding.swift +41 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/ViewEpoxyModeled.swift +10 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/README.md +31 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift +46 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingView.swift +391 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIIntrinsicContentSizeInvalidator.swift +44 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUILayoutMargins.swift +51 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift +172 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift +128 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift +452 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/SwiftUIView.swift +148 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift +40 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIViewConfiguringSwiftUIView.swift +43 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/BehaviorsConfigurableView.swift +45 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ContentConfigurableView.swift +36 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/EpoxyableView.swift +5 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/StyledView.swift +42 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ViewType.swift +51 -0
- package/Sources/Private/EmbeddedLibraries/README.md +27 -0
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+BackingConfiguration.swift +4 -4
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+MemoryFile.swift +2 -2
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Writing.swift +4 -4
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive.swift +8 -7
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Compression.swift +5 -5
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/FileManager+ZIP.swift +4 -4
- package/Sources/Private/MainThread/LayerContainers/CompLayers/ImageCompositionLayer.swift +6 -0
- package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +3 -1
- package/Sources/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.swift +18 -5
- package/Sources/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.swift +31 -3
- package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +33 -8
- package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +8 -1
- package/Sources/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.swift +13 -4
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerFontProvider.swift +2 -2
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerImageProvider.swift +1 -0
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTextProvider.swift +4 -4
- package/Sources/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift +3 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +2 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift +2 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +6 -2
- package/Sources/Private/Model/Assets/Asset.swift +8 -0
- package/Sources/Private/Model/Assets/AssetLibrary.swift +2 -2
- package/Sources/Private/Model/Assets/ImageAsset.swift +3 -3
- package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +16 -2
- package/Sources/Private/Model/DotLottie/DotLottieImageProvider.swift +20 -7
- package/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +26 -0
- package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +8 -5
- package/Sources/Private/Model/LayerEffects/DropShadowEffect.swift +45 -0
- package/Sources/Private/Model/LayerEffects/EffectValues/ColorEffectValue.swift +38 -0
- package/Sources/Private/Model/LayerEffects/EffectValues/EffectValue.swift +98 -0
- package/Sources/Private/Model/LayerEffects/EffectValues/Vector1DEffectValue.swift +38 -0
- package/Sources/Private/Model/LayerEffects/LayerEffect.swift +103 -0
- package/Sources/Private/Model/LayerStyles/DropShadowStyle.swift +72 -0
- package/Sources/Private/Model/LayerStyles/LayerStyle.swift +85 -0
- package/Sources/Private/Model/Layers/LayerModel.swift +27 -0
- package/Sources/Private/Model/Objects/Marker.swift +1 -1
- package/Sources/Private/Model/Objects/Transform.swift +1 -2
- package/Sources/Private/Model/ShapeItems/GradientFill.swift +1 -1
- package/Sources/Private/Model/ShapeItems/GradientStroke.swift +2 -2
- package/Sources/Private/Model/ShapeItems/Merge.swift +1 -1
- package/Sources/Private/Model/ShapeItems/ShapeItem.swift +31 -26
- package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +0 -9
- package/Sources/Private/Model/ShapeItems/Star.swift +1 -1
- package/Sources/Private/Model/Text/Font.swift +2 -2
- package/Sources/Private/Model/Text/Glyph.swift +1 -1
- package/Sources/Private/RootAnimationLayer.swift +2 -2
- package/Sources/Private/Utility/Debugging/LayerDebugging.swift +3 -1
- package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +32 -2
- package/Sources/Private/Utility/Helpers/AnimationContext.swift +4 -2
- package/Sources/Private/Utility/Helpers/AnyEquatable.swift +24 -0
- package/Sources/Private/Utility/Helpers/Binding+Map.swift +18 -0
- package/Sources/Private/Utility/Helpers/View+ValueChanged.swift +20 -0
- package/Sources/Private/Utility/LottieAnimationSource.swift +41 -0
- package/Sources/Private/Utility/Primitives/BezierPath.swift +2 -2
- package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +2 -2
- package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +1 -1
- package/Sources/Public/Animation/LottieAnimation.swift +21 -2
- package/Sources/Public/Animation/LottieAnimationLayer.swift +1464 -0
- package/Sources/Public/Animation/LottieAnimationView.swift +259 -736
- package/Sources/Public/Animation/LottiePlaybackMode.swift +117 -0
- package/Sources/Public/Animation/LottieView.swift +462 -0
- package/Sources/Public/AnimationCache/AnimationCacheProvider.swift +2 -1
- package/Sources/Public/AnimationCache/DefaultAnimationCache.swift +5 -6
- package/Sources/Public/Configuration/DecodingStrategy.swift +15 -0
- package/Sources/Public/Configuration/LottieConfiguration.swift +47 -0
- package/Sources/Public/Configuration/ReducedMotionOption.swift +114 -0
- package/Sources/Public/{LottieConfiguration.swift → Configuration/RenderingEngineOption.swift} +2 -57
- package/Sources/Public/{iOS → Controls}/AnimatedButton.swift +56 -13
- package/Sources/Public/{iOS → Controls}/AnimatedControl.swift +80 -8
- package/Sources/Public/{iOS → Controls}/AnimatedSwitch.swift +71 -31
- package/Sources/Public/Controls/LottieButton.swift +122 -0
- package/Sources/Public/Controls/LottieSwitch.swift +144 -0
- package/Sources/Public/Controls/LottieViewType.swift +79 -0
- package/Sources/Public/DotLottie/DotLottieConfiguration.swift +24 -0
- package/Sources/Public/DotLottie/DotLottieFile.swift +21 -6
- package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +79 -0
- package/Sources/Public/DynamicProperties/AnimationKeypath.swift +11 -1
- package/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +14 -0
- package/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +13 -0
- package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +22 -5
- package/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +14 -0
- package/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +13 -0
- package/Sources/Public/FontProvider/AnimationFontProvider.swift +8 -0
- package/Sources/Public/ImageProvider/AnimationImageProvider.swift +24 -0
- package/Sources/Public/Keyframes/Keyframe.swift +6 -0
- package/Sources/Public/Primitives/Vectors.swift +2 -2
- package/Sources/Public/TextProvider/AnimationTextProvider.swift +79 -6
- package/Sources/Public/iOS/AnimationSubview.swift +1 -1
- package/Sources/Public/iOS/BundleImageProvider.swift +16 -2
- package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +6 -4
- package/Sources/Public/iOS/FilepathImageProvider.swift +22 -3
- package/Sources/Public/iOS/LottieAnimationViewBase.swift +7 -1
- package/Sources/Public/macOS/BundleImageProvider.macOS.swift +15 -1
- package/Sources/Public/macOS/FilepathImageProvider.macOS.swift +21 -2
- package/lottie-ios.podspec +2 -1
- package/package.json +1 -1
- package/Sources/Private/Model/DotLottie/DotLottieConfiguration.swift +0 -15
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Helpers.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Progress.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Reading.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ReadingDeprecated.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+WritingDeprecated.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ZIP64.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+CompressionDeprecated.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Serialization.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+Serialization.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+ZIP64.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/README.md +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/URL+ZIP.swift +0 -0
|
@@ -38,11 +38,14 @@ struct LayerAnimationContext {
|
|
|
38
38
|
/// The logger that should be used for assertions and warnings
|
|
39
39
|
let logger: LottieLogger
|
|
40
40
|
|
|
41
|
+
/// Mutable state related to log events, stored on the `CoreAnimationLayer`.
|
|
42
|
+
let loggingState: LoggingState
|
|
43
|
+
|
|
41
44
|
/// The AnimationKeypath represented by the current layer
|
|
42
45
|
var currentKeypath: AnimationKeypath
|
|
43
46
|
|
|
44
|
-
/// The `
|
|
45
|
-
var textProvider:
|
|
47
|
+
/// The `AnimationKeypathTextProvider`
|
|
48
|
+
var textProvider: AnimationKeypathTextProvider
|
|
46
49
|
|
|
47
50
|
/// Records the given animation keypath so it can be logged or collected into a list
|
|
48
51
|
/// - Used for `CoreAnimationLayer.logHierarchyKeypaths()` and `allHierarchyKeypaths()`
|
|
@@ -84,3 +87,19 @@ struct LayerAnimationContext {
|
|
|
84
87
|
return copy
|
|
85
88
|
}
|
|
86
89
|
}
|
|
90
|
+
|
|
91
|
+
// MARK: - LoggingState
|
|
92
|
+
|
|
93
|
+
/// Mutable state related to log events, stored on the `CoreAnimationLayer`.
|
|
94
|
+
final class LoggingState {
|
|
95
|
+
|
|
96
|
+
// MARK: Lifecycle
|
|
97
|
+
|
|
98
|
+
init() { }
|
|
99
|
+
|
|
100
|
+
// MARK: Internal
|
|
101
|
+
|
|
102
|
+
/// Whether or not the warning about unsupported After Effects expressions
|
|
103
|
+
/// has been logged yet for this layer.
|
|
104
|
+
var hasLoggedAfterEffectsExpressionsWarning = false
|
|
105
|
+
}
|
|
@@ -67,6 +67,15 @@ class BaseCompositionLayer: BaseAnimationLayer {
|
|
|
67
67
|
inFrame: CGFloat(baseLayerModel.inFrame),
|
|
68
68
|
outFrame: CGFloat(baseLayerModel.outFrame),
|
|
69
69
|
context: context)
|
|
70
|
+
|
|
71
|
+
// There are two different drop shadow schemas, either using `DropShadowEffect` or `DropShadowStyle`.
|
|
72
|
+
// If both happen to be present, prefer the `DropShadowEffect` (which is the drop shadow schema
|
|
73
|
+
// supported on other platforms).
|
|
74
|
+
let dropShadowEffect = baseLayerModel.effects.first(where: { $0 is DropShadowEffect }) as? DropShadowModel
|
|
75
|
+
let dropShadowStyle = baseLayerModel.styles.first(where: { $0 is DropShadowStyle }) as? DropShadowModel
|
|
76
|
+
if let dropShadowModel = dropShadowEffect ?? dropShadowStyle {
|
|
77
|
+
try contentsLayer.addDropShadowAnimations(for: dropShadowModel, context: context)
|
|
78
|
+
}
|
|
70
79
|
}
|
|
71
80
|
}
|
|
72
81
|
|
|
@@ -9,7 +9,7 @@ import QuartzCore
|
|
|
9
9
|
struct LayerContext {
|
|
10
10
|
let animation: LottieAnimation
|
|
11
11
|
let imageProvider: AnimationImageProvider
|
|
12
|
-
let textProvider:
|
|
12
|
+
let textProvider: AnimationKeypathTextProvider
|
|
13
13
|
let fontProvider: AnimationFontProvider
|
|
14
14
|
let compatibilityTracker: CompatibilityTracker
|
|
15
15
|
var layerName: String
|
|
@@ -93,4 +93,6 @@ extension RepeaterTransform: TransformModel {
|
|
|
93
93
|
var _position: KeyframeGroup<LottieVector3D>? { position }
|
|
94
94
|
var _positionX: KeyframeGroup<LottieVector1D>? { nil }
|
|
95
95
|
var _positionY: KeyframeGroup<LottieVector1D>? { nil }
|
|
96
|
+
var _skew: KeyframeGroup<LottieVector1D>? { nil }
|
|
97
|
+
var _skewAxis: KeyframeGroup<LottieVector1D>? { nil }
|
|
96
98
|
}
|
|
@@ -41,7 +41,8 @@ final class ShapeLayer: BaseCompositionLayer {
|
|
|
41
41
|
if let repeater = shapeLayer.items.first(where: { $0 is Repeater }) as? Repeater {
|
|
42
42
|
try setUpRepeater(repeater, context: context)
|
|
43
43
|
} else {
|
|
44
|
-
|
|
44
|
+
let shapeItems = shapeLayer.items.map { ShapeItemLayer.Item(item: $0, groupPath: []) }
|
|
45
|
+
try setupGroups(from: shapeItems, parentGroup: nil, parentGroupPath: [], context: context)
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
|
|
@@ -50,7 +51,8 @@ final class ShapeLayer: BaseCompositionLayer {
|
|
|
50
51
|
let copyCount = Int(try repeater.copies.exactlyOneKeyframe(context: context, description: "repeater copies").value)
|
|
51
52
|
|
|
52
53
|
for index in 0..<copyCount {
|
|
53
|
-
|
|
54
|
+
let shapeItems = items.map { ShapeItemLayer.Item(item: $0, groupPath: []) }
|
|
55
|
+
for groupLayer in try makeGroupLayers(from: shapeItems, parentGroup: nil, parentGroupPath: [], context: context) {
|
|
54
56
|
let repeatedLayer = RepeaterLayer(repeater: repeater, childLayer: groupLayer, index: index)
|
|
55
57
|
addSublayer(repeatedLayer)
|
|
56
58
|
}
|
|
@@ -127,7 +129,7 @@ final class GroupLayer: BaseAnimationLayer {
|
|
|
127
129
|
private func setupLayerHierarchy(context: LayerContext) throws {
|
|
128
130
|
// Groups can contain other groups, so we may have to continue
|
|
129
131
|
// recursively creating more `GroupLayer`s
|
|
130
|
-
try setupGroups(from:
|
|
132
|
+
try setupGroups(from: items, parentGroup: group, parentGroupPath: groupPath, context: context)
|
|
131
133
|
|
|
132
134
|
// Create `ShapeItemLayer`s for each subgroup of shapes that should be rendered as a single unit
|
|
133
135
|
// - These groups are listed from front-to-back, so we have to add the sublayers in reverse order
|
|
@@ -183,7 +185,7 @@ extension CALayer {
|
|
|
183
185
|
/// - Each `Group` item becomes its own `GroupLayer` sublayer.
|
|
184
186
|
/// - Other `ShapeItem` are applied to all sublayers
|
|
185
187
|
fileprivate func setupGroups(
|
|
186
|
-
from items: [
|
|
188
|
+
from items: [ShapeItemLayer.Item],
|
|
187
189
|
parentGroup: Group?,
|
|
188
190
|
parentGroupPath: [String],
|
|
189
191
|
context: LayerContext)
|
|
@@ -204,29 +206,27 @@ extension CALayer {
|
|
|
204
206
|
/// - Each `Group` item becomes its own `GroupLayer` sublayer.
|
|
205
207
|
/// - Other `ShapeItem` are applied to all sublayers
|
|
206
208
|
fileprivate func makeGroupLayers(
|
|
207
|
-
from items: [
|
|
209
|
+
from items: [ShapeItemLayer.Item],
|
|
208
210
|
parentGroup: Group?,
|
|
209
211
|
parentGroupPath: [String],
|
|
210
212
|
context: LayerContext)
|
|
211
213
|
throws -> [GroupLayer]
|
|
212
214
|
{
|
|
213
|
-
var
|
|
214
|
-
|
|
215
|
-
.grouped(by: { $0 is Group })
|
|
215
|
+
var groupItems = items.compactMap { $0.item as? Group }.filter { !$0.hidden }
|
|
216
|
+
var otherItems = items.filter { !($0.item is Group) && !$0.item.hidden }
|
|
216
217
|
|
|
217
218
|
// Handle the top-level `shapeLayer.items` array. This is typically just a single `Group`,
|
|
218
219
|
// but in practice can be any combination of items. The implementation expects all path-drawing
|
|
219
220
|
// shape items to be managed by a `GroupLayer`, so if there's a top-level path item we
|
|
220
221
|
// have to create a placeholder group.
|
|
221
|
-
if parentGroup == nil, otherItems.contains(where: { $0.drawsCGPath }) {
|
|
222
|
-
groupItems = [Group(items: items, name: "")]
|
|
222
|
+
if parentGroup == nil, otherItems.contains(where: { $0.item.drawsCGPath }) {
|
|
223
|
+
groupItems = [Group(items: items.map { $0.item }, name: "")]
|
|
223
224
|
otherItems = []
|
|
224
225
|
}
|
|
225
226
|
|
|
226
227
|
// Any child items that wouldn't be included in a valid shape render group
|
|
227
228
|
// need to be applied to child groups (otherwise they'd be silently ignored).
|
|
228
229
|
let inheritedItemsForChildGroups = otherItems
|
|
229
|
-
.map { ShapeItemLayer.Item(item: $0, groupPath: parentGroupPath) }
|
|
230
230
|
.shapeRenderGroups(groupHasChildGroupsToInheritUnusedItems: !groupItems.isEmpty)
|
|
231
231
|
.unusedItems
|
|
232
232
|
|
|
@@ -235,8 +235,6 @@ extension CALayer {
|
|
|
235
235
|
let groupsInZAxisOrder = groupItems.reversed()
|
|
236
236
|
|
|
237
237
|
return try groupsInZAxisOrder.compactMap { group in
|
|
238
|
-
guard let group = group as? Group else { return nil }
|
|
239
|
-
|
|
240
238
|
var pathForChildren = parentGroupPath
|
|
241
239
|
if !group.name.isEmpty {
|
|
242
240
|
pathForChildren.append(group.name)
|
|
@@ -30,16 +30,31 @@ final class SolidLayer: BaseCompositionLayer {
|
|
|
30
30
|
super.init(layer: typedLayer)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// MARK: Internal
|
|
34
|
+
|
|
35
|
+
override func setupAnimations(context: LayerAnimationContext) throws {
|
|
36
|
+
try super.setupAnimations(context: context)
|
|
37
|
+
|
|
38
|
+
// Even though the Lottie json schema provides a fixed `solidLayer.colorHex` value,
|
|
39
|
+
// we still need to create a set of keyframes and go through the standard `CAAnimation`
|
|
40
|
+
// codepath so that this value can be customized using the custom `ValueProvider`s API.
|
|
41
|
+
try shapeLayer.addAnimation(
|
|
42
|
+
for: .fillColor,
|
|
43
|
+
keyframes: KeyframeGroup(solidLayer.colorHex.cgColor),
|
|
44
|
+
value: { $0 },
|
|
45
|
+
context: context)
|
|
46
|
+
}
|
|
47
|
+
|
|
33
48
|
// MARK: Private
|
|
34
49
|
|
|
35
50
|
private let solidLayer: SolidLayerModel
|
|
36
51
|
|
|
52
|
+
/// Render the fill color in a child `CAShapeLayer`
|
|
53
|
+
/// - Using a `CAShapeLayer` specifically, instead of a `CALayer` with a `backgroundColor`,
|
|
54
|
+
/// allows the size of the fill shape to be different from `contentsLayer.size`.
|
|
55
|
+
private let shapeLayer = CAShapeLayer()
|
|
56
|
+
|
|
37
57
|
private func setupContentLayer() {
|
|
38
|
-
// Render the fill color in a child `CAShapeLayer`
|
|
39
|
-
// - Using a `CAShapeLayer` specifically, instead of a `CALayer` with a `backgroundColor`,
|
|
40
|
-
// allows the size of the fill shape to be different from `contentsLayer.size`.
|
|
41
|
-
let shapeLayer = CAShapeLayer()
|
|
42
|
-
shapeLayer.fillColor = solidLayer.colorHex.cgColor
|
|
43
58
|
shapeLayer.path = CGPath(rect: .init(x: 0, y: 0, width: solidLayer.width, height: solidLayer.height), transform: nil)
|
|
44
59
|
addSublayer(shapeLayer)
|
|
45
60
|
}
|
|
@@ -44,9 +44,19 @@ final class TextLayer: BaseCompositionLayer {
|
|
|
44
44
|
context: textAnimationContext,
|
|
45
45
|
description: "text layer text")
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
// Prior to Lottie 4.3.0 the Core Animation rendering engine always just used `LegacyAnimationTextProvider`
|
|
48
|
+
// but incorrectly called it with the full keypath string, unlike the Main Thread rendering engine
|
|
49
|
+
// which only used the last component of the keypath. Starting in Lottie 4.3.0 we use `AnimationKeypathTextProvider`
|
|
50
|
+
// instead if implemented.
|
|
51
|
+
if let keypathTextValue = context.textProvider.text(for: textAnimationContext.currentKeypath, sourceText: sourceText.text) {
|
|
52
|
+
renderLayer.text = keypathTextValue
|
|
53
|
+
} else if let legacyTextProvider = context.textProvider as? LegacyAnimationTextProvider {
|
|
54
|
+
renderLayer.text = legacyTextProvider.textFor(
|
|
55
|
+
keypathName: textAnimationContext.currentKeypath.fullPath,
|
|
56
|
+
sourceText: sourceText.text)
|
|
57
|
+
} else {
|
|
58
|
+
renderLayer.text = sourceText.text
|
|
59
|
+
}
|
|
50
60
|
|
|
51
61
|
renderLayer.sizeToFit()
|
|
52
62
|
}
|
|
@@ -36,6 +36,7 @@ final class ValueProviderStore {
|
|
|
36
36
|
properties. Supported properties are: \(supportedProperties.joined(separator: ", ")).
|
|
37
37
|
""")
|
|
38
38
|
|
|
39
|
+
valueProviders.removeAll(where: { $0.keypath == keypath })
|
|
39
40
|
valueProviders.append((keypath: keypath, valueProvider: valueProvider))
|
|
40
41
|
}
|
|
41
42
|
|
|
@@ -72,9 +73,11 @@ final class ValueProviderStore {
|
|
|
72
73
|
|
|
73
74
|
// Convert the type-erased keyframe values using this `CustomizableProperty`'s conversion closure
|
|
74
75
|
let typedKeyframes = typeErasedKeyframes.compactMap { typeErasedKeyframe -> Keyframe<Value>? in
|
|
75
|
-
guard let convertedValue = customizableProperty.conversion(typeErasedKeyframe.value) else {
|
|
76
|
+
guard let convertedValue = customizableProperty.conversion(typeErasedKeyframe.value, anyValueProvider) else {
|
|
76
77
|
logger.assertionFailure("""
|
|
77
|
-
Could not convert value of type \(type(of: typeErasedKeyframe.value)) to expected type \(
|
|
78
|
+
Could not convert value of type \(type(of: typeErasedKeyframe.value)) from \(anyValueProvider) to expected type \(
|
|
79
|
+
Value
|
|
80
|
+
.self)
|
|
78
81
|
""")
|
|
79
82
|
return nil
|
|
80
83
|
}
|
|
@@ -93,10 +96,9 @@ final class ValueProviderStore {
|
|
|
93
96
|
// MARK: Private
|
|
94
97
|
|
|
95
98
|
private let logger: LottieLogger
|
|
96
|
-
|
|
97
99
|
private var valueProviders = [(keypath: AnimationKeypath, valueProvider: AnyValueProvider)]()
|
|
98
100
|
|
|
99
|
-
/// Retrieves the most-recently-registered Value Provider that matches the given
|
|
101
|
+
/// Retrieves the most-recently-registered Value Provider that matches the given keypath.
|
|
100
102
|
private func valueProvider(for keypath: AnimationKeypath) -> AnyValueProvider? {
|
|
101
103
|
// Find the last keypath matching the given keypath,
|
|
102
104
|
// so we return the value provider that was registered most-recently
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
// Created by Laura Skelton on 11/25/16.
|
|
2
|
+
// Copyright © 2016 Airbnb. All rights reserved.
|
|
3
|
+
|
|
4
|
+
// MARK: - Collection
|
|
5
|
+
|
|
6
|
+
extension Collection where Element: Diffable, Index == Int {
|
|
7
|
+
|
|
8
|
+
/// Diffs two collections (e.g. `Array`s) of `Diffable` items, returning an `IndexChangeset`
|
|
9
|
+
/// representing the minimal set of changes to get from the other collection to this collection.
|
|
10
|
+
///
|
|
11
|
+
/// - Parameters:
|
|
12
|
+
/// - from other: The collection of old data.
|
|
13
|
+
func makeChangeset(from other: Self) -> IndexChangeset {
|
|
14
|
+
// Arranging the elements contiguously prior to diffing improves performance by ~40%.
|
|
15
|
+
let new = ContiguousArray(self)
|
|
16
|
+
let old = ContiguousArray(other)
|
|
17
|
+
|
|
18
|
+
/// The entries in both this and the other collection, keyed by their `dataID`s.
|
|
19
|
+
var entries = [AnyHashable: EpoxyEntry](minimumCapacity: new.count)
|
|
20
|
+
var duplicates = [EpoxyEntry]()
|
|
21
|
+
|
|
22
|
+
var newResults = ContiguousArray<NewRecord>()
|
|
23
|
+
newResults.reserveCapacity(new.count)
|
|
24
|
+
|
|
25
|
+
for index in new.indices {
|
|
26
|
+
let id = new[index].diffIdentifier
|
|
27
|
+
let entry = entries[id, default: EpoxyEntry()]
|
|
28
|
+
if entry.trackNewIndex(index) {
|
|
29
|
+
duplicates.append(entry)
|
|
30
|
+
}
|
|
31
|
+
entries[id] = entry
|
|
32
|
+
newResults.append(NewRecord(entry: entry))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
var oldResults = ContiguousArray<OldRecord>()
|
|
36
|
+
oldResults.reserveCapacity(old.count)
|
|
37
|
+
|
|
38
|
+
for index in old.indices {
|
|
39
|
+
let id = old[index].diffIdentifier
|
|
40
|
+
let entry = entries[id]
|
|
41
|
+
entry?.pushOldIndex(index)
|
|
42
|
+
oldResults.append(OldRecord(entry: entry))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for newIndex in new.indices {
|
|
46
|
+
let entry = newResults[newIndex].entry
|
|
47
|
+
if let oldIndex = entry.popOldIndex() {
|
|
48
|
+
let newItem = new[newIndex]
|
|
49
|
+
let oldItem = other[oldIndex]
|
|
50
|
+
|
|
51
|
+
if !oldItem.isDiffableItemEqual(to: newItem) {
|
|
52
|
+
entry.isUpdated = true
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
newResults[newIndex].correspondingOldIndex = oldIndex
|
|
56
|
+
oldResults[oldIndex].correspondingNewIndex = newIndex
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
var deletes = [Int]()
|
|
61
|
+
var deleteOffsets = [Int]()
|
|
62
|
+
deleteOffsets.reserveCapacity(old.count)
|
|
63
|
+
var runningDeleteOffset = 0
|
|
64
|
+
|
|
65
|
+
for index in old.indices {
|
|
66
|
+
deleteOffsets.append(runningDeleteOffset)
|
|
67
|
+
|
|
68
|
+
let record = oldResults[index]
|
|
69
|
+
|
|
70
|
+
if record.correspondingNewIndex == nil {
|
|
71
|
+
deletes.append(index)
|
|
72
|
+
runningDeleteOffset += 1
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
var inserts = [Int]()
|
|
77
|
+
var updates = [(Int, Int)]()
|
|
78
|
+
var moves = [(Int, Int)]()
|
|
79
|
+
var insertOffsets = [Int]()
|
|
80
|
+
insertOffsets.reserveCapacity(new.count)
|
|
81
|
+
var runningInsertOffset = 0
|
|
82
|
+
|
|
83
|
+
for index in new.indices {
|
|
84
|
+
insertOffsets.append(runningInsertOffset)
|
|
85
|
+
|
|
86
|
+
let record = newResults[index]
|
|
87
|
+
|
|
88
|
+
if let oldArrayIndex = record.correspondingOldIndex {
|
|
89
|
+
if record.entry.isUpdated {
|
|
90
|
+
updates.append((oldArrayIndex, index))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let insertOffset = insertOffsets[index]
|
|
94
|
+
let deleteOffset = deleteOffsets[oldArrayIndex]
|
|
95
|
+
if (oldArrayIndex - deleteOffset + insertOffset) != index {
|
|
96
|
+
moves.append((oldArrayIndex, index))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
} else {
|
|
100
|
+
inserts.append(index)
|
|
101
|
+
runningInsertOffset += 1
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
EpoxyLogger.shared.assert(
|
|
106
|
+
old.count + inserts.count - deletes.count == new.count,
|
|
107
|
+
"Failed sanity check for old count with changes matching new count.")
|
|
108
|
+
|
|
109
|
+
return IndexChangeset(
|
|
110
|
+
inserts: inserts,
|
|
111
|
+
deletes: deletes,
|
|
112
|
+
updates: updates,
|
|
113
|
+
moves: moves,
|
|
114
|
+
newIndices: oldResults.map { $0.correspondingNewIndex },
|
|
115
|
+
duplicates: duplicates.map { $0.newIndices })
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/// Diffs between two collections (eg. `Array`s) of `Diffable` items, and returns an `IndexPathChangeset`
|
|
119
|
+
/// representing the minimal set of changes to get from the other collection to this collection.
|
|
120
|
+
///
|
|
121
|
+
/// - Parameters:
|
|
122
|
+
/// - from other: The collection of old data.
|
|
123
|
+
/// - fromSection: The section the other collection's data exists within. Defaults to `0`.
|
|
124
|
+
/// - toSection: The section this collection's data exists within. Defaults to `0`.
|
|
125
|
+
func makeIndexPathChangeset(
|
|
126
|
+
from other: Self,
|
|
127
|
+
fromSection: Int = 0,
|
|
128
|
+
toSection: Int = 0)
|
|
129
|
+
-> IndexPathChangeset
|
|
130
|
+
{
|
|
131
|
+
let indexChangeset = makeChangeset(from: other)
|
|
132
|
+
|
|
133
|
+
return IndexPathChangeset(
|
|
134
|
+
inserts: indexChangeset.inserts.map { index in
|
|
135
|
+
[toSection, index]
|
|
136
|
+
},
|
|
137
|
+
deletes: indexChangeset.deletes.map { index in
|
|
138
|
+
[fromSection, index]
|
|
139
|
+
},
|
|
140
|
+
updates: indexChangeset.updates.map { fromIndex, toIndex in
|
|
141
|
+
([fromSection, fromIndex], [toSection, toIndex])
|
|
142
|
+
},
|
|
143
|
+
moves: indexChangeset.moves.map { fromIndex, toIndex in
|
|
144
|
+
([fromSection, fromIndex], [toSection, toIndex])
|
|
145
|
+
},
|
|
146
|
+
duplicates: indexChangeset.duplicates.map { duplicate in
|
|
147
|
+
duplicate.map { index in
|
|
148
|
+
[toSection, index]
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// Diffs between two collections (e.g. `Array`s) of `Diffable` items, returning an
|
|
154
|
+
/// `IndexSetChangeset` representing the minimal set of changes to get from the other collection
|
|
155
|
+
/// to this collection.
|
|
156
|
+
///
|
|
157
|
+
/// - Parameters:
|
|
158
|
+
/// - from other: The collection of old data.
|
|
159
|
+
func makeIndexSetChangeset(from other: Self) -> IndexSetChangeset {
|
|
160
|
+
let indexChangeset = makeChangeset(from: other)
|
|
161
|
+
|
|
162
|
+
return IndexSetChangeset(
|
|
163
|
+
inserts: .init(indexChangeset.inserts),
|
|
164
|
+
deletes: .init(indexChangeset.deletes),
|
|
165
|
+
updates: indexChangeset.updates,
|
|
166
|
+
moves: indexChangeset.moves,
|
|
167
|
+
newIndices: indexChangeset.newIndices,
|
|
168
|
+
duplicates: indexChangeset.duplicates.map { .init($0) })
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
extension Collection where Element: DiffableSection, Index == Int {
|
|
174
|
+
/// Diffs between two collections (e.g. `Array`s) of `DiffableSection` items, returning an
|
|
175
|
+
/// `SectionedChangeset` representing the minimal set of changes to get from the other collection
|
|
176
|
+
/// to this collection.
|
|
177
|
+
///
|
|
178
|
+
/// - Parameters:
|
|
179
|
+
/// - from other: The collection of old data.
|
|
180
|
+
func makeSectionedChangeset(from other: Self) -> SectionedChangeset {
|
|
181
|
+
let sectionChangeset = makeIndexSetChangeset(from: other)
|
|
182
|
+
var itemChangeset = IndexPathChangeset()
|
|
183
|
+
|
|
184
|
+
for fromSectionIndex in other.indices {
|
|
185
|
+
guard let toSectionIndex = sectionChangeset.newIndices[fromSectionIndex] else {
|
|
186
|
+
continue
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
let fromItems = other[fromSectionIndex].diffableItems
|
|
190
|
+
let toItems = self[toSectionIndex].diffableItems
|
|
191
|
+
|
|
192
|
+
let itemIndexChangeset = toItems.makeIndexPathChangeset(
|
|
193
|
+
from: fromItems,
|
|
194
|
+
fromSection: fromSectionIndex,
|
|
195
|
+
toSection: toSectionIndex)
|
|
196
|
+
|
|
197
|
+
itemChangeset += itemIndexChangeset
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return SectionedChangeset(sectionChangeset: sectionChangeset, itemChangeset: itemChangeset)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// MARK: - EpoxyEntry
|
|
205
|
+
|
|
206
|
+
/// A bookkeeping refrence type for the diffing algorithm.
|
|
207
|
+
private final class EpoxyEntry {
|
|
208
|
+
|
|
209
|
+
// MARK: Internal
|
|
210
|
+
|
|
211
|
+
private(set) var oldIndices = [Int]()
|
|
212
|
+
private(set) var newIndices = [Int]()
|
|
213
|
+
var isUpdated = false
|
|
214
|
+
|
|
215
|
+
/// Tracks an index from the new indices, returning `true` if this entry has previously tracked
|
|
216
|
+
/// a new index as a means to identify duplicates and `false` otherwise.
|
|
217
|
+
func trackNewIndex(_ index: Int) -> Bool {
|
|
218
|
+
let previouslyEmpty = newIndices.isEmpty
|
|
219
|
+
|
|
220
|
+
newIndices.append(index)
|
|
221
|
+
|
|
222
|
+
// We've encountered a duplicate, return true so we can track it.
|
|
223
|
+
if !previouslyEmpty, newIndices.count == 2 {
|
|
224
|
+
return true
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return false
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
func pushOldIndex(_ index: Int) {
|
|
231
|
+
oldIndices.append(index)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
func popOldIndex() -> Int? {
|
|
235
|
+
guard currentOldIndex < oldIndices.endIndex else {
|
|
236
|
+
return nil
|
|
237
|
+
}
|
|
238
|
+
defer {
|
|
239
|
+
currentOldIndex += 1
|
|
240
|
+
}
|
|
241
|
+
return oldIndices[currentOldIndex]
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// MARK: Private
|
|
245
|
+
|
|
246
|
+
private var currentOldIndex = 0
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// MARK: - OldRecord
|
|
250
|
+
|
|
251
|
+
/// A bookkeeping type for pairing up an old element with its new index.
|
|
252
|
+
private struct OldRecord {
|
|
253
|
+
var entry: EpoxyEntry?
|
|
254
|
+
var correspondingNewIndex: Int? = nil
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// MARK: - NewRecord
|
|
258
|
+
|
|
259
|
+
/// A bookkeeping type for pairing up a new element with its old index.
|
|
260
|
+
private struct NewRecord {
|
|
261
|
+
var entry: EpoxyEntry
|
|
262
|
+
var correspondingOldIndex: Int? = nil
|
|
263
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Created by Laura Skelton on 5/11/17.
|
|
2
|
+
// Copyright © 2017 Airbnb. All rights reserved.
|
|
3
|
+
|
|
4
|
+
// MARK: - Diffable
|
|
5
|
+
|
|
6
|
+
/// A protocol that allows us to check identity and equality between items for the purposes of
|
|
7
|
+
/// diffing.
|
|
8
|
+
protocol Diffable {
|
|
9
|
+
|
|
10
|
+
/// Checks for equality between items when diffing.
|
|
11
|
+
///
|
|
12
|
+
/// - Parameters:
|
|
13
|
+
/// - otherDiffableItem: The other item to check equality against while diffing.
|
|
14
|
+
func isDiffableItemEqual(to otherDiffableItem: Diffable) -> Bool
|
|
15
|
+
|
|
16
|
+
/// The identifier to use when checking identity while diffing.
|
|
17
|
+
var diffIdentifier: AnyHashable { get }
|
|
18
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Created by eric_horacek on 12/9/20.
|
|
2
|
+
// Copyright © 2020 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
// MARK: - DiffableSection
|
|
5
|
+
|
|
6
|
+
/// A protocol that allows us to check identity and equality between sections of `Diffable` items
|
|
7
|
+
/// for the purposes of diffing.
|
|
8
|
+
protocol DiffableSection: Diffable {
|
|
9
|
+
/// The diffable items in this section.
|
|
10
|
+
associatedtype DiffableItems: Collection where
|
|
11
|
+
DiffableItems.Index == Int,
|
|
12
|
+
DiffableItems.Element: Diffable
|
|
13
|
+
|
|
14
|
+
/// The diffable items in this section.
|
|
15
|
+
var diffableItems: DiffableItems { get }
|
|
16
|
+
}
|