From 52b22e89536098f8e6e6b867f099de99b70b0137 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Mon, 15 Apr 2024 13:33:13 -0700 Subject: [PATCH] Fix issue where animation size could be incorrect after loading async animation (#2379) --- .github/workflows/main.yml | 11 +++++---- Example/Example/AnimationListView.swift | 10 ++++++-- .../Example/LottieViewLayoutDemoView.swift | 15 ++++++++++-- Sources/Public/Animation/LottieView.swift | 23 +++++++++++++++---- 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0d24524206..9934800e23 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,6 +33,7 @@ jobs: matrix: xcode: - '15.2' # Swift 5.9 + - '15.3' # Swift 5.10 steps: - uses: actions/checkout@v2 - uses: ./.github/actions/setup @@ -49,7 +50,7 @@ jobs: - uses: actions/checkout@v2 - uses: ./.github/actions/setup with: - xcode: '15.2' # Swift 5.9 + xcode: '15.3' # Swift 5.10 - name: Build Example run: bundle exec rake build:example:all @@ -60,7 +61,7 @@ jobs: - uses: actions/checkout@v2 - uses: ./.github/actions/setup with: - xcode: '15.2' # Swift 5.9 + xcode: '15.3' # Swift 5.10 - name: Test Package run: bundle exec rake test:package - name: Process test artifacts @@ -150,7 +151,7 @@ jobs: strategy: matrix: xcode: - - '15.2' # Swift 5.9, first Xcode version with visionOS + - '15.3' # Swift 5.10 steps: - uses: actions/checkout@v2 - uses: ./.github/actions/setup @@ -166,7 +167,7 @@ jobs: strategy: matrix: xcode: - - '15.2' # Swift 5.9 + - '15.3' # Swift 5.10 steps: - uses: actions/checkout@v2 - uses: ./.github/actions/setup @@ -185,7 +186,7 @@ jobs: with: install-mint: true install-carthage: true - xcode: '15.2' # Swift 5.9 + xcode: '15.3' # Swift 5.10 - name: Test Carthage support run: bundle exec rake test:carthage diff --git a/Example/Example/AnimationListView.swift b/Example/Example/AnimationListView.swift index 0873e0271c..1f87b123fc 100644 --- a/Example/Example/AnimationListView.swift +++ b/Example/Example/AnimationListView.swift @@ -35,7 +35,7 @@ struct AnimationListView: View { Text(item.name) } - case .animationList, .controlsDemo, .swiftUIInteroperability: + case .animationList, .controlsDemo, .swiftUIInteroperability, .lottieViewLayoutDemo: Text(item.name) .frame(height: 50) } @@ -52,6 +52,8 @@ struct AnimationListView: View { ControlsDemoView() case .swiftUIInteroperability: SwiftUIInteroperabilityDemoView() + case .lottieViewLayoutDemo: + LottieViewLayoutDemoView() } } } @@ -72,7 +74,7 @@ struct AnimationListView: View { guard let url = urls.first else { return nil } return await LottieAnimation.loadedFrom(url: url)?.animationSource - case .animationList, .controlsDemo, .swiftUIInteroperability: + case .animationList, .controlsDemo, .swiftUIInteroperability, .lottieViewLayoutDemo: return nil } } @@ -104,6 +106,7 @@ extension AnimationListView { case remoteAnimations(name: String, urls: [URL]) case controlsDemo case swiftUIInteroperability + case lottieViewLayoutDemo } var items: [Item] { @@ -159,6 +162,7 @@ extension AnimationListView { .animationList(.remoteAnimationsDemo), .controlsDemo, .swiftUIInteroperability, + .lottieViewLayoutDemo, ] } } @@ -174,6 +178,8 @@ extension AnimationListView.Item { return "Controls Demo" case .swiftUIInteroperability: return "SwiftUI Interoperability Demo" + case .lottieViewLayoutDemo: + return "LottieView Layout Demo" } } } diff --git a/Example/Example/LottieViewLayoutDemoView.swift b/Example/Example/LottieViewLayoutDemoView.swift index a0b6bbfda3..8091ce4a1d 100644 --- a/Example/Example/LottieViewLayoutDemoView.swift +++ b/Example/Example/LottieViewLayoutDemoView.swift @@ -4,7 +4,7 @@ import Lottie import SwiftUI -struct ContentView: View { +struct LottieViewLayoutDemoView: View { var body: some View { HStack { VStack { @@ -35,7 +35,18 @@ struct ContentView: View { LottieView(animation: .named("Samples/LottieLogo1")) .looping() - Text("intrinsic content size") + Text("automatic size") + } + + VStack { + LottieView { + try await Task.sleep(for: .seconds(1)) + return LottieAnimation.named("Samples/LottieLogo1") + } + .intrinsicSize() + .looping() + + Text("intrinsic size, async") } } .frame(maxWidth: .infinity, maxHeight: .infinity) diff --git a/Sources/Public/Animation/LottieView.swift b/Sources/Public/Animation/LottieView.swift index dc0e59e882..9147b65a76 100644 --- a/Sources/Public/Animation/LottieView.swift +++ b/Sources/Public/Animation/LottieView.swift @@ -125,7 +125,7 @@ public struct LottieView: UIViewConfiguringSwiftUIView { } .sizing(sizing) .configure { context in - applyCurrentAnimationConfiguration(to: context.view) + applyCurrentAnimationConfiguration(to: context.view, in: context.container) } .configurations(configurations) .opacity(animationSource == nil ? 0 : 1) @@ -152,14 +152,22 @@ public struct LottieView: UIViewConfiguringSwiftUIView { return copy } - /// Returns a copy of this view that can be resized by scaling its animation to fit the size - /// offered by its parent. + /// Returns a copy of this view that can be resized by scaling its animation + /// to always fit the size offered by its parent. public func resizable() -> Self { var copy = self copy.sizing = .proposed return copy } + /// Returns a copy of this view that adopts the intrinsic size of the animation, + /// up to the proposed size. + public func intrinsicSize() -> Self { + var copy = self + copy.sizing = .intrinsic + return copy + } + @available(*, deprecated, renamed: "playing()", message: "Will be removed in a future major release.") public func play() -> Self { playbackMode(.playing(.fromProgress(nil, toProgress: 1, loopMode: .playOnce))) @@ -501,7 +509,10 @@ public struct LottieView: UIViewConfiguringSwiftUIView { } /// Applies playback configuration for the current animation to the `LottieAnimationView` - private func applyCurrentAnimationConfiguration(to view: LottieAnimationView) { + private func applyCurrentAnimationConfiguration( + to view: LottieAnimationView, + in container: SwiftUIMeasurementContainer) + { guard let animationSource else { return } var imageProviderConfiguration = imageProviderConfiguration var playbackMode = playbackMode @@ -543,6 +554,10 @@ public struct LottieView: UIViewConfiguringSwiftUIView { if animationSource.animation !== view.animation { view.loadAnimation(animationSource) animationDidLoad?(animationSource) + + // Invalidate the intrinsic size of the SwiftUI measurement container, + // since any cached measurements will be out of date after updating the animation. + container.invalidateIntrinsicContentSize() } if