View — Section

在 SwiftUI 中,Section 是一种非常重要的视图,用于在列表视图(List)中进行内容分组。它可以带来更清晰的结构和更好的用户体验,特别是在带有大量数据的应用中。以下是对 Section 使用的详细介绍,包括各种配置和实现悬停头部(sticky headers)的效果。

基本使用 #

Section 可以用来分组列表项,指定头部和尾部视图。

示例代码:基本使用 #

import SwiftUI

struct ContentView: View {
    let sections = [
        SectionData(title: "First Section", items: ["Item 1", "Item 2", "Item 3"]),
        SectionData(title: "Second Section", items: ["Item 4", "Item 5", "Item 6"]),
        SectionData(title: "Third Section", items: ["Item 7", "Item 8", "Item 9"])
    ]

    var body: some View {
        List {
            ForEach(sections) { section in
                Section(header: Text(section.title)) {
                    ForEach(section.items, id: \.self) { item in
                        Text(item)
                    }
                }
            }
        }
    }
}

struct SectionData: Identifiable {
    let id = UUID()
    let title: String
    let items: [String]
}

解释: #

  1. 数据模型

    • SectionData:数据模型,包括 titleitems
    • sections:示例数据数组,包含多个 SectionData
  2. SectionForEach

    • 使用 ForEach 遍历 sections 数组,为每个分组创建一个 Section
    • Sectionheader 设置为每个分组的标题文本。
    • 内嵌的 ForEach 用于遍历每个分组中的项目。

自定义头部和尾部视图 #

Section 可以自定义头部和尾部视图,通过提供不同的视图可以实现更复杂的布局。

示例代码:自定义头部和尾部 #

import SwiftUI

struct ContentView: View {
    let sections = [
        SectionData(title: "First Section", footer: "End of First Section", items: ["Item 1", "Item 2", "Item 3"]),
        SectionData(title: "Second Section", footer: "End of Second Section", items: ["Item 4", "Item 5", "Item 6"]),
        SectionData(title: "Third Section", footer: "End of Third Section", items: ["Item 7", "Item 8", "Item 9"])
    ]

    var body: some View {
        List {
            ForEach(sections) { section in
                Section(
                    header: CustomHeaderView(title: section.title),
                    footer: CustomFooterView(footer: section.footer)
                ) {
                    ForEach(section.items, id: \.self) { item in
                        Text(item)
                    }
                }
            }
        }
    }
}

struct CustomHeaderView: View {
    let title: String

    var body: some View {
        Text(title)
            .font(.headline)
            .padding()
            .background(Color.gray.opacity(0.2))
            .cornerRadius(10)
    }
}

struct CustomFooterView: View {
    let footer: String

    var body: some View {
        Text(footer)
            .font(.footnote)
            .padding()
            .background(Color.gray.opacity(0.2))
            .cornerRadius(10)
    }
}

struct SectionData: Identifiable {
    let id = UUID()
    let title: String
    let footer: String
    let items: [String]
}

解释: #

  1. 自定义视图

    • CustomHeaderViewCustomFooterView 用于定制头部和尾部视图。
    • 使用 .background().cornerRadius() 修饰符设置背景和圆角效果。
  2. Section 配置

    • Sectionheaderfooter 分别设置为自定义视图。

实现分组悬停(Sticky Headers)效果 #

在 UITableView 中,分组头部可以悬停在视图顶部保持可见。在 SwiftUI 中,这种 sticky headers 效果可以通过一些配置和调整实现。

示例代码:实现分组悬停 #

import SwiftUI

struct ContentView: View {
    let sections = [
        SectionData(title: "First Section", items: Array(1...10).map { "Item \($0)" }),
        SectionData(title: "Second Section", items: Array(1...10).map { "Item \($0)" }),
        SectionData(title: "Third Section", items: Array(1...10).map { "Item \($0)" })
    ]
    
    var body: some View {
        NavigationView {
            List {
                ForEach(sections) { section in
                    Section(header: StickyHeader(text: section.title)) {
                        ForEach(section.items, id: \.self) { item in
                            Text(item)
                        }
                    }
                }
            }
            .navigationTitle("Sticky Headers")
            .listStyle(InsetGroupedListStyle()) // 选择合适的列表样式, 提供 better默认展示形式
        }
    }
}

struct StickyHeader: View {
    let text: String

    var body: some View {
        HStack {
            Text(text)
                .font(.headline)
                .padding(.leading)
                .padding(.vertical, 10)
                .frame(maxWidth: .infinity, alignment: .leading)
                .background(Color(UIColor.systemBackground).opacity(0.9)) // 设置背景色,确保悬停效果
                .offset(y: -8)
            Spacer()
        }
        .background(Color(UIColor.systemBackground)) // 避免透明感叠
    }
}

struct SectionData: Identifiable {
    let id = UUID()
    let title: String
    let items: [String]
}

解释: #

  1. 数据部分

    • sections 包含示例分组数据。
    • 每个分组有一个标题和多个子项。
  2. 实现概念与细节

    • 使用 InsetGroupedListStyle() 提供了更好的默认展示形式。
    • StickyHeader 组件必须包含背景色,且用 .opacity(0.9) 确保悬停时效果一致且不过度叠加。
    • .offset(y:-8) 是为了避免视觉错位。

注意事项#

  • 性能:对于大量列表项,确保对 SectionList 使用 Identifiable 数据模型,提高性能。
  • 导航栏组合:在导航栏下方实现悬停效果时,可能需要根据不同的项目布局进行视觉上的微调。
  • 合适样式:利用 .listStyle() 选择合适效果的列表样式,如避免常规填满,采取 InsetGroupedListStyle() 或类似样式。

组合以上多方面信息和代码,旨在实现 SwiftUI 中拥有优秀分组及悬停效果的 List视图,为用户带来更佳的体验与展示效果。

在 SwiftUI 中,可以使用 ForEach 结合 DisclosureGroup 来实现一个可展开和折叠的 Section。下面是一个完整的示例,展示如何实现一个动态的、可折叠的 Section


完整示例:可折叠的 Section #

import SwiftUI

struct CollapsibleSectionExample: View {
    @State private var isExpanded = false // 折叠状态
    @State private var items = ["Item 1", "Item 2", "Item 3"] // 数据项

    var body: some View {
        NavigationView {
            List {
                DisclosureGroup(isExpanded: $isExpanded) { // 可折叠部分
                    ForEach(items, id: \.self) { item in
                        Text(item) // 展示每个数据项
                    }
                } label: {
                    Text("Expandable Section") // 折叠标题
                        .font(.headline)      // 设置标题风格
                        .foregroundColor(.primary)
                }
            }
            .navigationTitle("Collapsible Section")
        }
    }
}

代码说明: #

  1. DisclosureGroup

    • 这是 SwiftUI 提供的内置控件,用于实现可折叠/展开区域。
    • isExpanded:绑定折叠状态的布尔变量(true 表示展开、false 表示折叠)。
    • label:设置折叠区域的标题。
  2. 动态数据:

    • 通过 ForEach 动态生成内容,可以灵活地实现可折叠区域中多个内容项的展示。
  3. List + NavigationView 整合:

    • 使用 ListDisclosureGroup 嵌套在一个导航列表框架中,更贴近真实应用。

运行效果 #

isExpanded = true

  • 可展开的部分会显示所有 ForEach 元素(如 Item 1Item 2Item 3)。

isExpanded = false

  • 折叠部分会只显示标题 "Expandable Section"

改进示例:支持多个 Section #

如果你有多个可折叠的 Section,可以通过 ForEachBinding 管理每个 Section 的折叠状态。

代码实现 #

struct MultiCollapsibleSectionExample: View {
    @State private var sections = [
        SectionData(title: "Fruits", items: ["Apple", "Banana", "Cherry"], isExpanded: true),
        SectionData(title: "Vehicles", items: ["Car", "Bike", "Bus"], isExpanded: false),
        SectionData(title: "Animals", items: ["Dog", "Cat", "Elephant"], isExpanded: true)
    ]

    var body: some View {
        NavigationView {
            List {
                ForEach($sections) { $section in
                    DisclosureGroup(isExpanded: $section.isExpanded) {
                        ForEach(section.items, id: \.self) { item in
                            Text(item)
                        }
                    } label: {
                        Text(section.title)
                            .font(.headline)
                            .foregroundColor(.primary)
                    }
                }
            }
            .navigationTitle("Collapsible Sections")
        }
    }
}

struct SectionData: Identifiable {
    let id = UUID()
    let title: String
    let items: [String]
    var isExpanded: Bool
}

代码说明: #

  1. 数据模型:

    • 创建 SectionData 数据结构,包含 titleitems 和可折叠状态 isExpanded
  2. 动态折叠:

    • 使用 $sections 的双向绑定,使每个 Section 的折叠状态独立管理。
  3. UI 特性:

    • DisclosureGroupForEach 的嵌套使展示多个折叠组变得简单。

效果 #

  • 多个可折叠 Section:
    • 每个 Section 包括 title 和若干子项。
    • 点击 title 可以展开或折叠当前 Section。
  • 独立管理:
    • 每个 Section 的折叠状态均由 isExpanded 独立控制。

扩展功能:添加动态变化 #

为了增加功能性,可以允许用户动态地添加、删除 Section 或相关内容。


示例:动态添加内容到 Section #

struct DynamicCollapsibleSectionExample: View {
    @State private var sections = [
        SectionData(title: "Fruits", items: ["Apple", "Banana"], isExpanded: true),
        SectionData(title: "Vehicles", items: ["Car", "Bike"], isExpanded: false)
    ]
    
    var body: some View {
        NavigationView {
            List {
                ForEach($sections) { $section in
                    DisclosureGroup(isExpanded: $section.isExpanded) {
                        ForEach(section.items, id: \.self) { item in
                            Text(item)
                        }
                        
                        Button("Add Item") {
                            section.items.append("New Item \(section.items.count + 1)")
                        }
                        .font(.caption)
                        .foregroundColor(.blue)
                    } label: {
                        Text(section.title)
                            .font(.headline)
                            .foregroundColor(.primary)
                    }
                }
            }
            .navigationTitle("Dynamic Sections")
            .toolbar {
                Button(action: addSection) {
                    Image(systemName: "plus")
                }
            }
        }
    }
    
    private func addSection() {
        sections.append(SectionData(title: "New Section \(sections.count + 1)", items: [], isExpanded: false))
    }
}

新增功能: #

  1. 添加新的 Section:

    • 使用 Toolbar 的添加按钮动态地创建新 Section。
    • 每个新 Section 的默认标题为 "New Section X",并从折叠状态开始。
  2. 动态更新 Section 内容:

    • 每个 Section 内提供一个 Add Item 按钮,用于在当前 Section 添加新内容。

效果 #

  • 可以动态创建新的 Section,并向现有 Section 添加新内容,提供更强的交互能力。

总结 #

SwiftUI 中使用 DisclosureGroup 是构建可折叠 Section 的最佳方式。以下功能可以灵活实现:

  1. 单个可折叠 Section:
    • 使用 DisclosureGroup 配合布尔状态变量。
  2. 多个折叠 Section:
    • 使用模型(例如 SectionData)和 ForEach 动态生成多个区域。
  3. 动态数据:
    • 动态地更新 Section 的内容或新增 Section,实现更复杂的交互需求。

希望这个例子帮助您实现适合自己的折叠 Section!

本文共 2348 字,上次修改于 Jan 23, 2025