Search code examples
listanimationswiftuisections

SwiftUI animate list section rows


I try to add custom animation when list section show and hide its rows, the animation is when rows appear its will slide from top to bottom and when the section rows disappear the rows slide from bottom to top.

The code:

struct AnimateListRows: View {
    
    let rowValues = Array(0...10)
    @State private var showSection = false
    
    var body: some View {
        List {
            ForEach(rowValues, id: \.self) {
                    ListRow(rowValue: $0)
            }
            Section {
                if showSection {
                    ForEach(rowValues, id: \.self) {
                        ListRow(rowValue: $0)
                    }
                }
            } header: {
                HStack {
                    Text("Section")
                    Spacer()
                    Image(systemName: "button.programmable")
                        .foregroundColor(.blue)
                        .onTapGesture {
                            withAnimation(.easeIn(duration: 0.3)) {
                            showSection.toggle()
                            }
                        }
                }
            }
            ForEach(rowValues, id: \.self) {
                    ListRow(rowValue: $0)
            }
        }
    }
}

struct AnimateListRows_Previews: PreviewProvider {
    static var previews: some View {
        AnimateListRows()
    }
}

Solution

  • I tried your example with an iPhone 14 simulator using this dummy implementation of ListRow:

    struct ListRow: View {
        var rowValue: Int
        var body: some View {
            Text("\(rowValue)")
        }
    }
    

    The rows are revealed as you described. So I assume that you don't want this, instead you want some kind of custom transition which you have not described?

    My understanding is that List doesn't give you much control over transitions, but there may be some ways to work around this limitation:

    • You currently have one outer List with three ForEach groups. If you were to use an outer VStack and then have a nested List for each of the data sets then you could apply a transition to the List that is being shown dynamically. Something like:
        if showSection {
            List {
                ForEach(rowValues, id: \.self) {
                    ListRow(rowValue: $0)
                }
            }
            .transition(.move(edge: .trailing))
        }
    

    However, you'll need to find a different way to toggle the visibility, because the section header doesn't work well.

    • Alternatively, you may be able to achieve a bit more animation by using modifiers such as .opacity and .offset on the ForEach. There are other SO answers that do this, eg see How can I animate individual rows in SwiftUI List?.

    • Otherwise, if you want full control of the transitions then you may be better going for a VStack instead of a List.