View — OutlineGroup

OutlineGroup 是 SwiftUI 中用于展示树状结构数据的视图,它能够递归地渲染层级数据,非常适合展示文件目录、组织结构图等。它与 List 视图类似,但更专注于处理具有父子关系的数据。

OutlineGroup 的使用方式

OutlineGroup 主要通过以下方法使用:

OutlineGroup(data, id: \.id, children: \.children) { item in
    // 显示单个 item 的视图
}
  • data: 这是一个符合 Identifiable 协议的集合,表示树状结构的根节点数据。
  • id: 一个 KeyPath,用于标识数据中的唯一性,类似于 List 中的 id 参数。
  • children: 一个 KeyPath,指向数据中包含子节点的属性。子节点属性必须是可选类型 (Optional),例如 [TreeItem]?,空值表示该节点是叶子节点。
  • content: 一个闭包,用于定义如何显示单个节点的视图。

示例

为了更好地理解 OutlineGroup 的使用,我们来看一个具体的例子。假设我们有一个文件目录结构,可以用以下结构体表示:

struct FileItem: Identifiable {
    let id = UUID()
    let name: String
    var children: [FileItem]?
}

然后,我们可以创建一个示例数据:

let fileData = [
    FileItem(name: "Documents", children: [
        FileItem(name: "Report.pdf"),
        FileItem(name: "Design.sketch"),
        FileItem(name: "Project", children: [
            FileItem(name: "README.md"),
            FileItem(name: "main.swift")
        ])
    ]),
    FileItem(name: "Photos", children: [
        FileItem(name: "IMG_001.jpg"),
        FileItem(name: "IMG_002.jpg")
    ])
]

使用 OutlineGroup 展示该数据:

OutlineGroup(fileData, id: \.id, children: \.children) { item in
    HStack {
        Image(systemName: item.children == nil ? "doc.fill" : "folder.fill")
        Text(item.name)
    }
}

这段代码会递归地渲染 fileData 中的数据,创建可展开的列表视图。具有子节点的项(例如 “Documents” 和 “Project”)会显示一个 disclosure indicator,点击可以展开或折叠子节点。

与 List 结合使用

OutlineGroup 也可以与 List 结合使用,以获得列表样式的外观。尤其是在侧边栏导航中,这种组合非常实用。

List(fileData, id: \.id, children: \.children) { item in
    HStack {
        Image(systemName: item.children == nil ? "doc.fill" : "folder.fill")
        Text(item.name)
    }
}
.listStyle(.sidebar) // 侧边栏样式

进阶用法:自定义 DisclosureGroup

OutlineGroup 内部使用 DisclosureGroup 来实现展开和折叠功能。你可以通过自定义 DisclosureGroup 来实现更精细的控制。

总结

OutlineGroup 提供了一种简洁而强大的方式来展示树状结构数据。通过理解其基本用法和与 List 的结合,你可以轻松地创建具有良好用户体验的层级列表视图。 1 2 4 6

import SwiftUI

struct FileItem: Identifiable {
    let id = UUID()
    let name: String
    var children: [FileItem]?
}

struct ContentView: View {
    let fileData = [
        FileItem(name: "Documents", children: [
            FileItem(name: "Report.pdf"),
            FileItem(name: "Design.sketch"),
            FileItem(name: "Project", children: [
                FileItem(name: "README.md"),
                FileItem(name: "main.swift")
            ])
        ]),
        FileItem(name: "Photos", children: [
            FileItem(name: "IMG_001.jpg"),
            FileItem(name: "IMG_002.jpg")
        ])
    ]

    var body: some View {
        List(fileData, id: \.id, children: \.children) { item in
            HStack {
                Image(systemName: item.children == nil ? "doc.fill" : "folder.fill")
                Text(item.name)
            }
        }
        .listStyle(.sidebar)
    }
}

核心概念 #

OutlineGroup 是 SwiftUI 中用于展示树形层级结构数据的视图容器,特别适合需要展开/折叠交互的场景(如文件目录、分类菜单等)。它自动处理展开状态,并支持递归渲染嵌套数据。以下是使用详解:

  • 适用场景:多层级数据(如文件夹结构、分类导航、组织架构)。
  • 自动状态管理:用户点击时会自动切换展开/折叠状态,无需手动管理。
  • 递归渲染:无需为每一层编写重复代码,自动处理子节点。

基本用法 #

1. 数据结构 #

确保数据模型满足以下条件:

  • 遵循 Identifiable 协议(每个元素有唯一标识)。
  • 通过某个属性(如 children)指向子节点(可选数组,nil 表示叶节点)。
struct FileItem: Identifiable {
    let id = UUID()
    var name: String
    var children: [FileItem]? // 非 nil 表示可展开
}

// 示例数据
let fileTree: [FileItem] = [
    FileItem(name: "文档", children: [
        FileItem(name: "工作", children: [
            FileItem(name: "项目1.pdf", children: nil),
            FileItem(name: "报告.doc", children: nil)
        ]),
        FileItem(name: "个人", children: [
            FileItem(name: "照片", children: [...]),
        ])
    ])
]

2. 创建 OutlineGroup #

ListOutlineGroup 中直接使用:

List {
    OutlineGroup(fileTree, children: \.children) { item in
        Text(item.name)
            .padding(.leading, 8)
    }
}
.listStyle(.sidebar) // 可选样式(如侧边栏风格)

高级用法 #

自定义展开图标 #

通过 Label 和系统图标增强交互提示:

OutlineGroup(fileTree, children: \.children) { item in
    Label {
        Text(item.name)
    } icon: {
        Image(systemName: item.children != nil ? "folder" : "doc")
    }
}

控制展开状态 #

手动管理展开状态(需使用 IdentifiedArray 等可标识集合):

@State private var expandedIDs: Set<UUID> = []

OutlineGroup(fileTree, children: \.children, id: \.id, expanded: $expandedIDs) { item in
    // ...
}

结合 DisclosureGroup #

混合使用实现更复杂交互(如添加自定义按钮):

OutlineGroup(fileTree, children: \.children) { item in
    DisclosureGroup {
        // 子内容
    } label: {
        Text(item.name)
        Button("更多") { /* ... */ }
    }
}

对比 DisclosureGroup #

  • OutlineGroup:自动递归、适合动态层级数据。
  • DisclosureGroup:手动控制单个展开状态、适合固定层级。

示例效果 #

会渲染为类似系统的文件浏览器,用户点击文件夹图标可展开/折叠子项,列表自动适应层级缩进。


注意事项 #

  1. 数据不可变:直接修改 children 可能不触发更新,建议使用 @State 或绑定到可观察对象。
  2. 性能优化:极深层级数据考虑使用 LazyVStack 或分页加载。
  3. 样式调整:通过 .listRowInsets.indentationLevel 控制缩进。

通过 OutlineGroup 可以快速实现复杂的树形交互界面,减少状态管理代码,提升开发效率。

本文共 1603 字,上次修改于 Feb 3, 2025