英文:
CardStack and Lottie
问题
我正在处理动画。它有一个堆栈上面有Lottie动画的卡片。CardStack库用于卡片,Lottie用于Lottie。
在CardStack库中,配置参数设置为显示2张卡片(当前和下一张),但尽管如此,只有在视图更改状态时才会释放内存。
内存累积到最后一张卡片。
因此,如果有100张卡片,就会出现问题。最重要的是,没有内存泄漏。
我尝试了各种与Lottie库一起使用的加速器,但没有帮助。
至少应该朝哪个方向看,以免浪费太多内存?在其他操作系统上一切正常,所以问题不在于动画。
这是我的UIViewRepresentable
的代码:
struct CustomLottieView: UIViewRepresentable {
enum LoadingType {
case url(URL)
case data(DataResource)
}
let type: LoadingType
let isPaused: Bool
let card: CardModel?
let animationView = LottieAnimationView()
let transition: BoundCardType?
init(type: LoadingType, isPaused: Bool = false, card: CardModel? = nil, transition: BoundCardType? = nil) {
self.isPaused = isPaused
self.type = type
self.card = card
self.transition = transition
}
func makeUIView(context: Context) -> UIView {
let view = UIView(frame: .zero)
switch type {
case .url(let url):
LottieAnimation.loadedFrom(url: url) { animation in
animationView.animation = animation
isPaused ? nil : animationView.play()
animationView.loopMode = .loop
if let skillCardModel = card as? SkillCardModel {
animationView.play(marker: String(skillCardModel.currentLevelIndex + 1))
}
}
case .data(let data):
animationView.animation = .named(data.name, bundle: data.bundle)
if let skillCardModel = card as? SkillCardModel {
animationView.play(marker: String(skillCardModel.currentLevelIndex + 1))
}
}
animationView.contentMode = .scaleAspectFit
animationView.play()
view.addSubview(animationView)
animationView.translatesAutoresizingMaskIntoConstraints = false
animationView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
animationView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
if isPaused {
context.coordinator.parent.animationView.pause()
} else {
if let skillCardModel = card as? SkillCardModel {
if !context.coordinator.parent.animationView.isAnimationPlaying || animationView.animation == nil {
context.coordinator.parent.animationView.play(marker: String(skillCardModel.currentLevelIndex + 1))
} else {
withAnimation {
var transitionMarker = ""
if let transition = transition {
switch transition {
case .left:
transitionMarker = context.coordinator.parent.animationView.animation?.markerNames.first(where: { $0.contains(">\(skillCardModel.currentLevelIndex + 2)") }) ?? ""
context.coordinator.parent.animationView.play(fromMarker: String(skillCardModel.currentLevelIndex + 2), toMarker: transitionMarker, loopMode: .playOnce)
case .right:
transitionMarker = context.coordinator.parent.animationView.animation?.markerNames.first(where: { $0.contains("\(skillCardModel.currentLevelIndex)>") }) ?? ""
context.coordinator.parent.animationView.play(fromMarker: transitionMarker, toMarker: String(skillCardModel.currentLevelIndex + 1), loopMode: .playOnce)
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
context.coordinator.parent.animationView.play(marker: String(skillCardModel.currentLevelIndex + 1), loopMode: .loop)
}
}
}
} else {
context.coordinator.parent.animationView.play()
}
}
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
struct Coordinator {
var parent: CustomLottieView
}
}
希望这能帮助你解决内存问题。如果你有任何其他问题,请随时提问。
英文:
I'm working on animations. It has a stack of scarts on which are Lottie animations. The CardStack library is used for cards, and Lottie is used for Lottie.
In the CardStack library, the configuration parameter is set to show 2 cards (current and next), but despite this, memory is released only when the view changes state.
Memory accumulates up to the very last card.
Accordingly, if 100 cards arrive, then there will be trouble. And most importantly, there are no memory leaks.
I tried various accelerators with Lottie libraries and it did not help.
In which direction at least I should look, as to not waste so much memory? Everything works fine on the other operating system, so the problem is not with animation.
Here is my code for the UIViewRepresentable
:
struct CustomLottieView: UIViewRepresentable {
enum LoadingType {
case url(URL)
case data(DataResource)
}
let type: LoadingType
let isPaused: Bool
let card: CardModel?
let animationView = LottieAnimationView()
let transition: BoundCardType?
init(type: LoadingType, isPaused: Bool = false, card: CardModel? = nil, transition: BoundCardType? = nil) {
self.isPaused = isPaused
self.type = type
self.card = card
self.transition = transition
}
func makeUIView(context: Context) -> UIView {
let view = UIView(frame: .zero)
switch type {
case .url(let url):
LottieAnimation.loadedFrom(url: url) { animation in
animationView.animation = animation
isPaused ? nil : animationView.play()
animationView.loopMode = .loop
if let skillCardModel = card as? SkillCardModel {
animationView.play(marker: String(skillCardModel.currentLevelIndex + 1))
}
}
case .data(let data):
animationView.animation = .named(data.name, bundle: data.bundle)
if let skillCardModel = card as? SkillCardModel {
animationView.play(marker: String(skillCardModel.currentLevelIndex + 1))
}
}
animationView.contentMode = .scaleAspectFit
animationView.play()
view.addSubview(animationView)
animationView.translatesAutoresizingMaskIntoConstraints = false
animationView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
animationView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
if isPaused {
context.coordinator.parent.animationView.pause()
} else {
if let skillCardModel = card as? SkillCardModel {
if !context.coordinator.parent.animationView.isAnimationPlaying || animationView.animation == nil {
context.coordinator.parent.animationView.play(marker: String(skillCardModel.currentLevelIndex + 1))
} else {
withAnimation {
var transitionMarker = ""
if let transition = transition {
switch transition {
case .left:
transitionMarker = context.coordinator.parent.animationView.animation?.markerNames.first(where: { $0.contains(">\(skillCardModel.currentLevelIndex + 2)") }) ?? ""
context.coordinator.parent.animationView.play(fromMarker: String(skillCardModel.currentLevelIndex + 2), toMarker: transitionMarker, loopMode: .playOnce)
case .right:
transitionMarker = context.coordinator.parent.animationView.animation?.markerNames.first(where: { $0.contains("\(skillCardModel.currentLevelIndex)>") }) ?? ""
context.coordinator.parent.animationView.play(fromMarker: transitionMarker, toMarker: String(skillCardModel.currentLevelIndex + 1), loopMode: .playOnce)
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
context.coordinator.parent.animationView.play(marker: String(skillCardModel.currentLevelIndex + 1), loopMode: .loop)
}
}
}
} else {
context.coordinator.parent.animationView.play()
}
}
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
struct Coordinator {
var parent: CustomLottieView
}
}
答案1
得分: 1
以下是您要翻译的内容:
"你正在描述CardStack库的一个特性;它将所有卡片项数据模型以及每个关联的视图存储起来。即使某个卡片视图此刻没有显示在屏幕上(展开状态),它仍然会被存储在内存中。
这是有道理的;这些视图存储在一个延迟加载的属性中,当加载时,它将为每个卡片项生成一个视图。换句话说,这些视图一次性生成并在CardStack的生命周期内保留。
听起来你更希望CardStack库的行为更像TableView或CollectionView,根据需要动态生成视图,并在它们移出屏幕时销毁它们。这并不是CardStack的实现方式。
我建议分叉CardStack库,并更改视图生成和存储的方式。你仍然需要预生成每个卡片的视图(因为你需要一些视觉效果出现在堆栈中),但每个卡片的内部内容不需要在卡片即将显示在屏幕上(展开状态)之前生成。当卡片移出屏幕(折叠状态)时,你可以从内存中移除这个内容视图。"
英文:
You're describing a characteristic of the CardStack Library; it stores all the card item data models as well as each associated View. Even if a card view isn't being displayed onscreen (expanded) at the moment, it will still be stored in memory.
This makes sense; the views are stored in a lazy-loaded property, which when loaded will generate a view for each card item. In other words, the views are generated all-at-once and stored for the duration of the CardStack's lifecycle.
It sounds like you'd prefer the CardStack library to act more like TableView or CollectionView, dynamically generating views as they are needed and destroying them as they move offscreen. That's not how CardStack has been implemented.
I suggest forking the CardStack library, and changing how views are generated and stored. You'll still need to pregenerate views for each card (since you need some kind of visual to appear in the stack), but the inner content of each card doesn't need to be generated until just before the card will appear onscreen (expanded). When a card goes offscreen (collapsed) you can remove this content view from memory.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论