Lottie 3.0
When wrapping an AnimationView inside an UIViewRepresentable to interface with SwiftUI it is expected that the frame size is applied correctly to AnimationView.
The scale of the AnimationView is based on the animation bounds and gets overwritten.
This is because SwiftUI prefers the intrinsicContentSize over any specified frames.
Temporary workaround is modifying lottie-ios in AnimationView.swift to report back the frame and not the animation bounds:
// MARK: - Public (UIView Overrides)
override public var intrinsicContentSize: CGSize {
if let animation = animation {
return self.frame.size
// return animation.bounds.size
}
return .zero
}
Example:
struct ContentView: View {
@EnvironmentObject var appContext: AppContext
var body: some View {
VStack {
VStack {
AnimationLottieView(animation: "door").aspectRatio(contentMode: .fit).frame(minWidth: 0, maxWidth: 20, minHeight:0, maxHeight: 100)
Spacer()
}
}
}
AnimationView in UIViewRepresentable:
import Lottie
import SwiftUI
struct AnimationLottieView: UIViewRepresentable {
var animation: String!
let animationView = AnimationView()
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: UIViewRepresentableContext<AnimationLottieView>) -> UIView {
let animationLoop = Animation.named(animation)
animationView.animation = animationLoop
animationView.loopMode = .loop
animationView.contentMode = .scaleAspectFit
animationView.play()
return animationView
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<AnimationLottieView>) {
// swiftlint:disable:next force_cast
let animView = uiView as! AnimationView
}
class Coordinator: NSObject {
var parent: AnimationLottieView
init(_ animationView: AnimationLottieView) {
self.parent = animationView
super.init()
}
}
}
I had the same problem but following solution did work for me without the need to change Lottie's code.
I created a container view and constrained the AnimationView to the container via Auto Layout. Hope that helps.
struct LottieView: UIViewRepresentable {
var name: String!
func makeUIView(context: UIViewRepresentableContext<LottieView>) -> UIView {
let view = UIView()
let animationView = AnimationView()
animationView.animation = Animation.named(name)
animationView.contentMode = .scaleAspectFit
animationView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(animationView)
NSLayoutConstraint.activate([
animationView.widthAnchor.constraint(equalTo: view.widthAnchor),
animationView.heightAnchor.constraint(equalTo: view.heightAnchor)
])
return view
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieView>) {
}
}
@reversepanda while that workaround does solve the problem, it isn't a real solution and hence makes the interface of the UIViewRepresentable incorrect. If you actually need to implement updateUIView to interact with the LottieView, you now need to iterate over uiView.subviews to find the LottieView instead of being able to directly interact with it.
Moreover, UIViewType should be LottieView, not UIView.
For this reason, the fix should be done inside Lottie instead of requiring a workaround outside of Lottie.
Most helpful comment
I had the same problem but following solution did work for me without the need to change Lottie's code.
I created a container view and constrained the
AnimationViewto the container via Auto Layout. Hope that helps.