如何在SwiftUI中创建父子元素类型不同的层级侧边栏?
SwiftUI实现支持不同类型元素的可选中层级侧边栏
完全可以用SwiftUI实现,不需要依赖AppKit。核心思路是通过统一枚举类型封装不同的数据源模型,结合DisclosureGroup实现层级折叠,同时处理选中状态。
步骤1:定义统一的侧边栏元素枚举
用枚举包裹MailServer和MailServerFolder,让它们统一遵循Identifiable协议,方便List统一处理:
enum SidebarItem: Identifiable { case server(MailServer) case folder(MailServerFolder) var id: UUID { switch self { case .server(let server): return server.id case .folder(let folder): return folder.id } } var name: String { switch self { case .server(let server): return server.name case .folder(let folder): return folder.name } } }
步骤2:改造视图实现层级与选中逻辑
利用DisclosureGroup构建层级结构,通过List(selection:)绑定选中状态,自定义行视图区分父/子元素样式:
struct TestView: View { let servers: [MailServer] @State private var selectedItemId: UUID? var body: some View { List(selection: $selectedItemId) { ForEach(servers) { server in // 父元素:带折叠功能的DisclosureGroup,label设为可选中的行 DisclosureGroup { // 子元素:遍历服务器下的文件夹 ForEach(server.folders) { folder in SidebarRow(item: .folder(folder), isSelected: selectedItemId == folder.id) .tag(folder.id) } } label: { SidebarRow(item: .server(server), isSelected: selectedItemId == server.id) .tag(server.id) } } } .listStyle(.sidebar) // 适配macOS侧边栏默认样式 } } // 自定义侧边栏行视图,区分父/子元素样式与选中状态 struct SidebarRow: View { let item: SidebarItem let isSelected: Bool var body: some View { HStack { Text(item.name) } .foregroundColor(isSelected ? .accentColor : .primary) .font(item is .server ? .headline : .body) // 父元素用更醒目字体 .padding(.vertical, 4) } }
关键说明
- 枚举
SidebarItem解决了不同类型数据源无法直接在List中混用的问题,同时保留了原模型的类型信息。 DisclosureGroup原生支持层级折叠,完全匹配macOS邮件侧边栏的交互逻辑。- 通过
List(selection:)和tag()实现选中状态的绑定,父/子元素都能被独立选中。 - 自定义
SidebarRow可以灵活调整样式,比如父元素加粗、选中时高亮等,贴合需求。
内容的提问来源于stack exchange,提问作者Alex S. Glomsaas
相关产品推荐
相关产品推荐

