LazyVStack 滚动停止动画
LazyVStack scrolling stops animations
此代码列出了 100 行,带有刷新图标动画。当使用 LazyVStack
而不是 VStack
时,向下滚动到此列表的底部后,所有动画都会停止,包括向后滚动时列表顶部的动画。 VStack
没有这样的问题。关于为什么会发生这种情况的任何想法以及是否有解决方法?
使用 Xcode 12.0.1,iOS 14,Swift 5
struct RefreshingList : View {
@State var isAnimating = false
var body: some View {
ScrollView {
LazyVStack { // <- change to VStack to have animations not stop
ForEach(0..<100, id: \.self) { i in
HStack {
Text("\(i) -> ")
self.button()
}
}
}
}
.onAppear {
self.isAnimating=true
}
}
func button() -> some View {
Button(action: {}, label: {
Image(systemName: "arrow.2.circlepath")
.rotationEffect(Angle(degrees: self.isAnimating ? 360.0 : 0.0))
.animation(Animation.linear(duration: 2.0)
.repeatForever(autoreverses: false))
})
}
}
struct RefreshingListView: View {
var body: some View {
ScrollView {
LazyVStack {
ForEach(0..<100) {
LabelView(id: [=10=])
.id([=10=])
}
}
.animation(
Animation
.linear(
duration: 2)
.repeatForever(
autoreverses: false))
}
}
}
struct LabelView: View {
@State var
animate = false
var id: Int
var body: some View {
Label(
title: {
Text("<- \(id)")
}, icon: {
Image(
systemName:
"arrow.2.circlepath")
.foregroundColor(.blue)
.padding(.horizontal)
.rotationEffect(
Angle(
degrees:
animate ?
360 : 0))
}
)
.onAppear {
animate
.toggle()
}
}
}
观察到屏幕上的视图发生变化时会出现动画。在 VStack
中,所有视图都是立即创建的,因此 SwiftUI 能够观察到所有视图的旋转值的变化。
在 LazyVStack
的情况下,视图仅在需要时创建,因此当旋转值在 .onAppear
中发生变化时,底部的视图不会出现在屏幕上。当它们确实出现在屏幕上时,它具有已经更改的值,因此不会发生动画。
解决此问题的一种方法是让按钮拥有 .isAnimating
@State
变量。然后任何时候创建一个,它都会是动画的。
struct RefreshingList : View {
var body: some View {
ScrollView {
LazyVStack {
ForEach(0..<100, id: \.self) { i in
HStack {
Text("\(i) -> ")
AnimatedButton()
}
}
}
}
}
}
struct AnimatedButton: View {
@State var isAnimating = false
var body: some View {
Button(action: {
}, label: {
Image(systemName: "arrow.2.circlepath")
.rotationEffect(Angle(degrees: self.isAnimating ? 360.0 : 0.0))
.animation(Animation.linear(duration: 2.0)
.repeatForever(autoreverses: false))
})
.onAppear {
isAnimating = true
}
}
}
此代码列出了 100 行,带有刷新图标动画。当使用 LazyVStack
而不是 VStack
时,向下滚动到此列表的底部后,所有动画都会停止,包括向后滚动时列表顶部的动画。 VStack
没有这样的问题。关于为什么会发生这种情况的任何想法以及是否有解决方法?
使用 Xcode 12.0.1,iOS 14,Swift 5
struct RefreshingList : View {
@State var isAnimating = false
var body: some View {
ScrollView {
LazyVStack { // <- change to VStack to have animations not stop
ForEach(0..<100, id: \.self) { i in
HStack {
Text("\(i) -> ")
self.button()
}
}
}
}
.onAppear {
self.isAnimating=true
}
}
func button() -> some View {
Button(action: {}, label: {
Image(systemName: "arrow.2.circlepath")
.rotationEffect(Angle(degrees: self.isAnimating ? 360.0 : 0.0))
.animation(Animation.linear(duration: 2.0)
.repeatForever(autoreverses: false))
})
}
}
struct RefreshingListView: View {
var body: some View {
ScrollView {
LazyVStack {
ForEach(0..<100) {
LabelView(id: [=10=])
.id([=10=])
}
}
.animation(
Animation
.linear(
duration: 2)
.repeatForever(
autoreverses: false))
}
}
}
struct LabelView: View {
@State var
animate = false
var id: Int
var body: some View {
Label(
title: {
Text("<- \(id)")
}, icon: {
Image(
systemName:
"arrow.2.circlepath")
.foregroundColor(.blue)
.padding(.horizontal)
.rotationEffect(
Angle(
degrees:
animate ?
360 : 0))
}
)
.onAppear {
animate
.toggle()
}
}
}
观察到屏幕上的视图发生变化时会出现动画。在 VStack
中,所有视图都是立即创建的,因此 SwiftUI 能够观察到所有视图的旋转值的变化。
在 LazyVStack
的情况下,视图仅在需要时创建,因此当旋转值在 .onAppear
中发生变化时,底部的视图不会出现在屏幕上。当它们确实出现在屏幕上时,它具有已经更改的值,因此不会发生动画。
解决此问题的一种方法是让按钮拥有 .isAnimating
@State
变量。然后任何时候创建一个,它都会是动画的。
struct RefreshingList : View {
var body: some View {
ScrollView {
LazyVStack {
ForEach(0..<100, id: \.self) { i in
HStack {
Text("\(i) -> ")
AnimatedButton()
}
}
}
}
}
}
struct AnimatedButton: View {
@State var isAnimating = false
var body: some View {
Button(action: {
}, label: {
Image(systemName: "arrow.2.circlepath")
.rotationEffect(Angle(degrees: self.isAnimating ? 360.0 : 0.0))
.animation(Animation.linear(duration: 2.0)
.repeatForever(autoreverses: false))
})
.onAppear {
isAnimating = true
}
}
}