SwiftUI animating expand and collapse of list rows

2019-07-09 23:13发布

问题:

I'm using SwiftUI to animate an expand and collapse in a list.

How can I get the height expansion of the section to animate smoothly like it would in UIKit with a tableview?

struct Rows: View {
    let rows = ["Row 1", "Row 2", "Row 3", "Row 4", "Row 5"]

    var body: some View {
        Section {
            ForEach(rows.identified(by: \.self)) { name in
                Text(name)
                    .lineLimit(nil)
            }
        }
    }
}

struct Header: View {

    @State var isExpanded: Bool = false

    var body: some View {

        VStack(alignment: .leading) {
            Button(action: {
                self.isExpanded.toggle()

            }) {
                Text(self.isExpanded ? "Collapse Me" : "Expand Me")
                    .font(.footnote)
            }

            if self.isExpanded {
                Rows().animation(.fluidSpring())
            }
        }
    }
}

struct ContentView : View {

    var body: some View {
        List(0...4) { _ in
            Header()
        }
    }
}

The animation seems to only apply to the text in the rows not the actual height or separator line growing to accommodate the new rows. The row text also seems to start animating from the very top of the row rather than where it appears in the view hierarchy. I need a smooth animation.

回答1:

try to implement it like this:

struct ContentView : View {

    @State var expanded:[Int:Bool] = [:]

    func isExpanded(_ id:Int) -> Bool {
        expanded[id] ?? false
    }

    var body: some View {
        NavigationView{
            List {
                ForEach(0...80) { section in
                    Section(header: CustomeHeader(name: "Section \(section)", color: Color.white).tapAction {
                        self.expanded[section] = !self.isExpanded(section)
                    }) {
                        if self.isExpanded(section) {
                            ForEach(0...30) { row in
                                Text("Row \(row)")
                            }
                        }
                    }
                }
            }
        }.navigationBarTitle(Text("Title"))
    }
}

struct CustomeHeader: View {
    let name: String
    let color: Color

    var body: some View {
        VStack {
            Spacer()
            HStack {
                Text(name)
                Spacer()
            }
            Spacer()
            Divider()
        }
        .padding(0)
            .background(color.relativeWidth(1.3))
            .frame(height: 50)
    }
}


回答2:

I implemented it like this: (It is with proper animation)

struct ExpandCollapseList : View {
    @State var sectionState: [Int:Bool] = [:]

    var body: some View {
        NavigationView{
            List{
                ForEach(1...6){ section in
                    Section(header: Text("Section \(section)").tapAction {
                        self.sectionState[section] = !self.isExpanded(section)
                    }) {
                        if self.isExpanded(section){
                            ForEach(1...4){ row in
                                Text("Row \(row)")
                            }
                        }
                    }
                }
            }.navigationBarTitle(Text("Expand/Collapse List"))
            .listStyle(.grouped)
        }
    }

    func isExpanded(_ section:Int) -> Bool {
        sectionState[section] ?? false
    }
}


标签: swiftui