Closed sieren closed 4 years ago
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
.
@davidpasztor check this implementation for your concern. I am also adding support for AutoPlay
//
// LottieAnimationView.swift
// TabView_POC
//
// Created by Sumit Anantwar on 30/04/2021.
//
import SwiftUI
import UIKit
import Lottie
struct LottieAnimationView : UIViewRepresentable {
@State private var isPlayingDefault = true
let filename: String
let loopMode: LottieLoopMode
let isPlaying: Binding<Bool>?
init(filename: String, loopMode: LottieLoopMode = .loop, isPlaying: Binding<Bool>? = nil) {
self.filename = filename
self.loopMode = loopMode
self.isPlaying = isPlaying
}
func makeUIView(context: Context) -> AnimationViewProxy {
let playing = self.isPlaying ?? $isPlayingDefault
return AnimationViewProxy(
filename: filename, loopMode: loopMode, isPlaying: playing.wrappedValue
)
}
func updateUIView(_ animationView: AnimationViewProxy, context: Context) {
let playing = self.isPlaying ?? $isPlayingDefault
if playing.wrappedValue {
animationView.play()
} else {
animationView.stop()
}
}
final class AnimationViewProxy : UIView {
private let animationView = AnimationView()
private var isAnimationPlaying: Bool = true
init(filename: String, loopMode: LottieLoopMode, isPlaying: Bool = true) {
super.init(frame: .zero)
animationView.animation = Animation.named(filename)
animationView.contentMode = .scaleAspectFit
animationView.loopMode = loopMode
self.isAnimationPlaying = isPlaying
if isPlaying {
self.play()
}
self.addSubview(animationView)
animationView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
animationView.widthAnchor.constraint(equalTo: self.widthAnchor),
animationView.heightAnchor.constraint(equalTo: self.heightAnchor)
])
NotificationCenter.default.addObserver(self,
selector: #selector(applicationWillEnterForeground),
name: UIApplication.willEnterForegroundNotification,
object: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func play() {
self.isAnimationPlaying = true
self.animationView.play()
}
func stop() {
self.isAnimationPlaying = false
self.animationView.stop()
}
@objc private func applicationWillEnterForeground() {
if self.isAnimationPlaying {
self.animationView.play()
}
}
override func willMove(toWindow newWindow: UIWindow?) {
guard let _ = newWindow else {
self.animationView.stop()
return
}
if self.isAnimationPlaying {
self.animationView.play()
}
}
}
}
Check these before submitting:
This issue is a:
Which Version of Lottie are you using?
Lottie 3.0
What Platform are you on?
What Language are you in?
Expected Behavior
When wrapping an AnimationView inside an
UIViewRepresentable
to interface with SwiftUI it is expected that the frame size is applied correctly to AnimationView.Actual Behavior
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.Code Example
Temporary workaround is modifying lottie-ios in
AnimationView.swift
to report back the frame and not the animation bounds:Example:
AnimationView in UIViewRepresentable:
Animation JSON