Search code examples
iosmacosswiftuilazyvgrid

SwiftUI LazyVGrid transition animation not as expected


I have a LazyVGrid that is defined like this:

let column = Array(repeating: GridItem(.flexible(minimum: 120, maximum: .infinity)), count: 3)
        
LazyVGrid(columns: column, alignment: .leading, spacing: 5) {
    ForEach(data, id: \.self.uniqueStableId) { _ in
        Color.red
             .transition(.move(edge: .trailing))
             .frame(height: 150)
    }
}

I have a button, that starts an animation effect:

func handleButtonTap() {
    for index in 0..<9 {
        DispatchQueue.main.asyncAfter(deadline: .now() + Double(1 * index)) {
            withAnimation(.easeInOut(duration: 1)) {
                let newItem = Item()
                self.data.append(newItem)                                                                                     
            }
        }
    }
}

The animation should add the new Red squares into the grid one by one with 1 second intervals using a slide effect, but there are 2 issues:

  1. even though I set a specific transition for the Red color view, the transition is ignored and the squares are added with a fade in effect

image1

  1. each time the first square in the row, is added in a new row on the grid, it appears from the middle of the row, and starts to slide vertically, the next squares in same row fade in

image2

It appears I'm setting the transition and animation in the wrong place, because even if remove the .transition from the Red view, it still animates it the same way. Can't understand what controls it

image3

Anyone has a hint how to adjust it to work properly? Thanks!


Solution

  • I can reproduce your issue, and I don't know why it happens.
    But I found a possible solution. If you wrap the LazyVGrid in s ScrollView it works:

    struct Item: Identifiable {
        let id = UUID()
    }
    
    struct ContentView: View {
        
        @State private var data: [Item] = []
        let column = Array(repeating: GridItem(.flexible()), count: 3)
        
        var body: some View {
            VStack {
                Button("Add") { handleButtonTap() }
                
                ScrollView {
                LazyVGrid(columns: column, alignment: .leading, spacing: 5) {
                    ForEach(data) { _ in
                        Color.red
                            .frame(height: 150)
                            .transition(.move(edge: .trailing))
                    }
                }
                Spacer()
                }
            }
        }
        
        func handleButtonTap() {
            for index in 0..<9 {
                DispatchQueue.main.asyncAfter(deadline: .now() + Double(1 * index)) {
                    withAnimation(.easeInOut(duration: 1)) {
                        let newItem = Item()
                        self.data.append(newItem)
                    }
                }
            }
        }
        
    }
    

    enter image description here