1

This code lists 100 rows with refresh icon animations. When using a LazyVStack instead of a VStack, after scrolling down to the bottom of this list all the animations stop, including those at the top of the list once you scroll back. No such issues with a VStack. Any ideas as to why this is happening and is there a workaround? Using 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))
        })
    }
}
andrewz
  • 4,729
  • 5
  • 49
  • 67

2 Answers2

2

Animation happens when a change to a view on screen is observed. In the VStack all of the views are created immediately so SwiftUI is able to observe the change in rotation value for all views.

In the LazyVStack case, the views are only created as needed, so the bottom ones aren't on screen when the rotation value changes in .onAppear. When they do appear on screen, it is with the already changed value, so no animation happens.

One way to fix this is to let the buttons own the .isAnimating @State variable. Then anytime one is created, it will be animating.

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
        }
    }
}
vacawama
  • 150,663
  • 30
  • 266
  • 294
0
struct RefreshingListView: View {
    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(0..<100) {
                    LabelView(id: $0)
                        .id($0)
                }
            }
            .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()
        }
    }
}
Dim Novo
  • 367
  • 3
  • 8
  • This kind of works but there's a different side effect now. When I scroll down to the bottom the animations continue fine. But when I scroll back up to the top, the animations go in reverse rotational direction (CCW vs CW). And if I scroll back down again, the animation reverses once again. The animations don't stop, but reverse direction. – andrewz Oct 02 '20 at 02:54