我正在使用SwiftUI在列表中设置展开和折叠动画。
如何获得截面的高度扩展以像使用表视图的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()
}
}
}
该动画似乎仅适用于行中的文本,而不适用于实际高度或分隔线的增长以容纳新行。行文本似乎也从行的顶部开始动画,而不是从视图层次结构中出现的位置开始。我需要一个流畅的动画。
答案 0 :(得分:3)
由于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()
}
}
答案 1 :(得分:2)
我是这样实现的:(带有适当的动画)
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
}
}
答案 2 :(得分:1)
尝试这样实现:
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)
}
}