Skip to content

Commit

Permalink
Add possibility to remove ValueProvider (#2474)
Browse files Browse the repository at this point in the history
  • Loading branch information
batanus authored Sep 10, 2024
1 parent ab4c5d9 commit 7a8279b
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Sources/Private/CoreAnimation/CoreAnimationLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,14 @@ extension CoreAnimationLayer: RootAnimationLayer {
rebuildCurrentAnimation()
}

func removeValueProvider(for keypath: AnimationKeypath) {
valueProviderStore.removeValueProvider(for: keypath)

// We need to rebuild the current animation after removing a value provider,
// since any existing `CAAnimation`s could now be out of date.
rebuildCurrentAnimation()
}

func getValue(for _: AnimationKeypath, atFrame _: AnimationFrameTime?) -> Any? {
logger.assertionFailure("""
The Core Animation rendering engine doesn't support querying values for individual frames
Expand Down
5 changes: 5 additions & 0 deletions Sources/Private/CoreAnimation/ValueProviderStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ final class ValueProviderStore {
valueProviders.append((keypath: keypath, valueProvider: valueProvider))
}

/// Removes all ValueProviders for the given `AnimationKeypath`
func removeValueProvider(for keypath: AnimationKeypath) {
valueProviders.removeAll(where: { $0.keypath.matches(keypath) })
}

/// Retrieves the custom value keyframes for the given property,
/// if an `AnyValueProvider` was registered for the given keypath.
func customKeyframes<Value>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,17 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
}
}

func removeValueProvider(for keypath: AnimationKeypath) {
for layer in animationLayers {
if let foundProperties = layer.nodeProperties(for: keypath) {
for property in foundProperties {
property.removeProvider()
}
layer.displayWithFrame(frame: presentation()?.currentFrame ?? currentFrame, forceUpdates: true)
}
}
}

func getValue(for keypath: AnimationKeypath, atFrame: CGFloat?) -> Any? {
for layer in animationLayers {
if
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ class NodeProperty<T>: AnyNodeProperty {
valueContainer.setNeedsUpdate()
}

func removeProvider() {
valueProvider = originalValueProvider
valueContainer.setNeedsUpdate()
}

func update(frame: CGFloat) {
typedContainer.setValue(valueProvider.value(frame: frame), forFrame: frame)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ protocol AnyNodeProperty {

/// Sets the value provider for the property.
func setProvider(provider: AnyValueProvider)

/// Removes the value provider for the property.
func removeProvider()
}

extension AnyNodeProperty {
Expand Down
1 change: 1 addition & 0 deletions Sources/Private/RootAnimationLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ protocol RootAnimationLayer: CALayer {
func logHierarchyKeypaths()
func allHierarchyKeypaths() -> [String]
func setValueProvider(_ valueProvider: AnyValueProvider, keypath: AnimationKeypath)
func removeValueProvider(for keypath: AnimationKeypath)
func getValue(for keypath: AnimationKeypath, atFrame: AnimationFrameTime?) -> Any?
func getOriginalValue(for keypath: AnimationKeypath, atFrame: AnimationFrameTime?) -> Any?

Expand Down
10 changes: 10 additions & 0 deletions Sources/Public/Animation/LottieAnimationLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,16 @@ public class LottieAnimationLayer: CALayer {
animationLayer.setValueProvider(valueProvider, keypath: keypath)
}

public func removeValueProvider(for keypath: AnimationKeypath) {
guard let animationLayer = rootAnimationLayer else { return }

valueProviders.forEach {
guard $0.key.matches(keypath) else { return }
valueProviders[$0.key] = nil
}
animationLayer.removeValueProvider(for: keypath)
}

/// Reads the value of a property specified by the Keypath.
/// Returns nil if no property is found.
///
Expand Down
6 changes: 6 additions & 0 deletions Sources/Public/Animation/LottieAnimationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,12 @@ open class LottieAnimationView: LottieAnimationViewBase {
lottieAnimationLayer.setValueProvider(valueProvider, keypath: keypath)
}

/// Sets a ValueProvider for the specified keypath. The value provider will be removed
/// on all properties that match the keypath.
public func removeValueProvider(for keypath: AnimationKeypath) {
lottieAnimationLayer.removeValueProvider(for: keypath)
}

/// Reads the value of a property specified by the Keypath.
/// Returns nil if no property is found.
///
Expand Down
5 changes: 5 additions & 0 deletions Sources/Public/Controls/AnimatedControl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,11 @@ open class AnimatedControl: LottieControlType {
animationView.setValueProvider(valueProvider, keypath: keypath)
}

/// Removes a ValueProvider for the specified keypath
public func removeValueProvider(for keypath: AnimationKeypath) {
animationView.removeValueProvider(for: keypath)
}

// MARK: Internal

var stateMap: [UInt: String] = [:]
Expand Down
19 changes: 19 additions & 0 deletions Tests/ValueProvidersTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,24 @@ final class ValueProvidersTests: XCTestCase {
for: "Layer.Shape Group.Stroke 1.Color",
context: animationContext)
XCTAssertEqual(keyFramesQuery4?.keyframes.map(\.value.components), [[0, 0, 0, 1]])

// Test removing specific keypath
let keypathToRemove: AnimationKeypath = "**.Color"
store.setValueProvider(ColorValueProvider(.black), keypath: keypathToRemove)
store.removeValueProvider(for: keypathToRemove)
let keyFramesQuery5 = try store.customKeyframes(
of: .color,
for: "Layer.Shape Group.Stroke 1.Color",
context: animationContext)
XCTAssertNil(keyFramesQuery5)

// Test removing wildcard keypath
store.setValueProvider(ColorValueProvider(.black), keypath: "**1.Color")
store.removeValueProvider(for: "**.Color")
let keyFramesQuery6 = try store.customKeyframes(
of: .color,
for: "Layer.Shape Group.Stroke 1.Color",
context: animationContext)
XCTAssertNil(keyFramesQuery6)
}
}

0 comments on commit 7a8279b

Please sign in to comment.