【问题标题】:SwiftUI animating expand and collapse of list rowsSwiftUI 动画列表行的展开和折叠
【发布时间】:2019-06-28 07:16:26
【问题描述】:

我正在使用 SwiftUI 为列表中的展开和折叠设置动画。

如何使部分的高度扩展像在带有 tableview 的 UIKit 中一样平滑地设置动画?

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()
        }
    }
}

动画似乎只适用于行中的文本,而不是实际高度或分隔线为适应新行而增长。行文本似乎也从行的最顶部开始动画,而不是它出现在视图层次结构中的位置。我需要一个流畅的动画。

【问题讨论】:

    标签: swiftui


    【解决方案1】:

    我是这样实现的:(它带有适当的动画)

    struct ExpandCollapseList : View {
        @State var sectionState: [Int: Bool] = [:]
    
        var body: some View {
            NavigationView {
                List {
                    ForEach(1 ... 6, id: \.self) { section in
                        Section(header: Text("Section \(section)").onTapGesture {
                            self.sectionState[section] = !self.isExpanded(section)
                        }) {
                            if self.isExpanded(section) {
                                ForEach(1 ... 4, id: \.self) { row in
                                    Text("Row \(row)")
                                }
                            }
                        }
                    }
                }
                .navigationBarTitle(Text("Expand/Collapse List"))
                .listStyle(GroupedListStyle())
            }
        }
    
        func isExpanded(_ section: Int) -> Bool {
            sectionState[section] ?? false
        }
    }
    

    【讨论】:

    • 这不再适用于 Xcode 11 GM。更改它以修复它:tapAction -> onTapGesture forEach(1...6) -> forEach(1...6, id: \.self) .listStyle(.grouped) -> .listStyle(GroupedListStyle())
    • 当我折叠一个部分时,列表会将其滚动位置重置为初始状态。例如,当我折叠列表中的最后一个部分时,列表会向后滚动并显示前几个部分。在更新列表时保留滚动位置的任何想法?
    【解决方案2】:

    感谢Aakash Jaiswal's answer,我能够扩展此实现以满足我扩展到三层的需要,即部分、子部分和课程。编译器无法在单个 View 中编译整个实现,这就是我将其分开的原因。

    import SwiftUI
    
    struct MenuView: View {
        var body: some View {
            HStack {
                List {
                    ToggleableMenuItemsView(sections: menuItems)
                    .padding()
                }
            }
            .background(Color("Gray"))
            .cornerRadius(30)
            .padding(.top, 30)
            .padding(.trailing, bounds.width * 0.2)
            .padding(.bottom, 30)
            .shadow(radius: 10)
        }
    
        @State var menuItemState = [String: Bool]()
        private var bounds: CGRect { UIScreen.main.bounds }
    
        private func isExpanded(_ menuItem: MenuItem) -> Bool {
            menuItemState[menuItem.id] ?? false
        }
    }
    
    struct ToggleableMenuItemsView: View {
        let sections: [MenuItem]
    
        var body: some View {
            ForEach(sections) { section in
                Section(
                    header: Text(section.title)
                        .font(.title)
                        .onTapGesture { self.menuItemState[section.id] = !self.isExpanded(section) },
                    content: {
                        if self.isExpanded(section) {
                            ForEach(section.children) { subsection in
                                Section(
                                    header: Text(subsection.title)
                                        .font(.headline)
                                        .onTapGesture { self.menuItemState[subsection.id] = !self.isExpanded(subsection) },
                                    content: {
                                        if self.isExpanded(subsection) {
                                            LessonsListView(lessons: subsection.children)
                                        }
                                    }
                                )
                            }
                        }
                    }
                )
            }
        }
    
        @State var menuItemState = [String: Bool]()
    
        private func isExpanded(_ menuItem: MenuItem) -> Bool {
            menuItemState[menuItem.id] ?? false
        }
    }
    
    struct LessonsListView: View {
        let lessons: [MenuItem]
    
        var body: some View {
            ForEach(lessons) { lesson in
                Text(lesson.title)
                    .font(.subheadline)
            }
        }
    }
    
    class MenuItem: Identifiable {
        var id: String
        let title: String
        var children: [MenuItem]
    
        init(id: String, title: String, children: [MenuItem] = []) {
            self.id = id
            self.title = title
            self.children = children
        }
    }
    
    let menuItems = [
        MenuItem(
            id: "01",
            title: "The Land in its World",
            children: [
                MenuItem(
                    id: "01A",
                    title: "North and South",
                    children: [
                        MenuItem(
                            id: "01A01",
                            title: "Between Continents"
                        ),
                        MenuItem(
                            id: "01A02",
                            title: "The Wet North"
                        ),
                        MenuItem(
                            id: "01A03",
                            title: "The Dry South"
                        ),
                        MenuItem(
                            id: "01A04",
                            title: "Between Wet and Dry"
                        )
                    ]
                ),
                MenuItem(
                    id: "01B",
                    title: "East and West",
                    children: [
                        MenuItem(
                            id: "01B01",
                            title: "Sea and Desert"
                        ),
                        MenuItem(
                            id: "01B02",
                            title: "Exchange in Aram"
                        ),
                        MenuItem(
                            id: "01B03",
                            title: "Exchange in Egypt"
                        ),
                        MenuItem(
                            id: "01B04",
                            title: "A Bypass Between"
                        )
                    ]
                ),
                MenuItem(
                    id: "01C",
                    title: "Between Empires",
                    children: [
                        MenuItem(
                            id: "01C01",
                            title: "Imperial Dreams"
                        ),
                        MenuItem(
                            id: "01C02",
                            title: "Egypt Marches"
                        ),
                        MenuItem(
                            id: "01C03",
                            title: "Taking Egypt's Wealth"
                        ),
                        MenuItem(
                            id: "01C04",
                            title: "The Land Between"
                        )
                    ]
                )
            ]
        )
    ]
    
    struct MenuView_Previews: PreviewProvider {
        static var previews: some View {
            MenuView()
        }
    }
    

    Here's a demo

    【讨论】:

      【解决方案3】:

      尝试像这样实现它:

      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)
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2015-07-18
        • 1970-01-01
        • 2011-09-12
        • 1970-01-01
        • 2020-02-22
        • 2017-03-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多