You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SwiftUI中创建类似Notion的可展开切换列表?

SwiftUI实现类似Notion的可折叠Toggle列表

你当前使用List的children参数生成的是系统默认折叠列表,样式和交互逻辑和Notion的toggle list不符。要实现目标效果,需要自定义折叠状态管理和行样式,以下是修正后的代码:

struct Bookmark: Identifiable {
    let id = UUID()
    let name: String
    let icon: String
    var items: [Bookmark]?

    // 修正示例数据的引用错误
    static let apple = Bookmark(name: "1", icon: "1.circle")
    static let bbc = Bookmark(name: "2", icon: "square.and.pencil")

    static let example1 = Bookmark(name: "September", icon: "star", items: [apple, bbc])
    static let example2 = Bookmark(name: "2023", icon: "timer", items: [apple, bbc])
}

struct ContentView: View {
    let items: [Bookmark] = [.example1, .example2]
    @State private var collapsedIds: Set<UUID> = [] // 记录分组的折叠状态

    var body: some View {
        List {
            ForEach(items) { item in
                // 渲染分组行
                BookmarkRow(item: item, isCollapsed: collapsedIds.contains(item.id)) {
                    collapsedIds.toggle(item.id) // 切换折叠状态
                }

                // 展开时渲染子项,添加缩进体现层级
                if !collapsedIds.contains(item.id), let subItems = item.items {
                    ForEach(subItems) { subItem in
                        BookmarkRow(item: subItem, isCollapsed: false) {}
                            .padding(.leading, 20)
                    }
                }
            }
        }
        .listStyle(.plain) // 去除默认列表背景,贴近Notion简洁风格
    }
}

// 自定义行视图,集成折叠按钮、图标和文字
struct BookmarkRow: View {
    let item: Bookmark
    let isCollapsed: Bool
    let onToggle: () -> Void

    var body: some View {
        Button(action: onToggle) {
            HStack(spacing: 8) {
                // 折叠/展开箭头
                Image(systemName: isCollapsed ? "chevron.right" : "chevron.down")
                    .font(.caption)
                    .foregroundColor(.gray)
                // 自定义图标
                Image(systemName: item.icon)
                Text(item.name)
            }
            .foregroundColor(.primary)
        }
        .buttonStyle(.plain) // 保留原生列表行的交互触感
    }
}

// 扩展Set快速切换元素存在状态
extension Set where Element: Hashable {
    mutating func toggle(_ element: Element) {
        if contains(element) {
            remove(element)
        } else {
            insert(element)
        }
    }
}

核心改动说明:

  • 状态管理:用@State private var collapsedIds记录每个分组的折叠状态,通过UUID唯一标识分组
  • 自定义行视图:BookmarkRow集成折叠箭头按钮,点击时触发状态切换,同时保留自定义图标和文字布局
  • 层级缩进:子项添加padding(.leading, 20)实现层级缩进,和Notion的列表层级一致
  • 样式优化:使用.listStyle(.plain)去除系统默认的分组背景,让列表更简洁
  • 代码修正:修复原示例数据中错误的引用(Bookmark.1改为Bookmark.apple)

内容的提问来源于stack exchange,提问作者Jojo Who

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 00:45:14