SwiftUI层级列表OnTapGesture实现分类与子分类联动显示
SwiftUI层级列表点击时同时获取父分类与子分类信息的解决方案
问题描述
在SwiftUI的层级列表中使用onTapGesture时遇到以下问题:
- 点击无子类的分类(如Housing),「Selected Category」区域能正确显示该分类的图标和名称;
- 点击带子类的分类下的子项(如Bills下的Telephone),仅能获取子分类的名称,无法关联父分类的信息。
需要实现的效果:
- 点击无子类的分类:仅显示该分类的图标和名称;
- 点击子类:同时显示父分类的图标、名称,以及该子分类的名称。
解决方案
步骤1:定义选中状态的枚举类型
用枚举区分两种选中场景,清晰存储不同类型的分类信息:
enum Selection { case standalone(icon: String, name: String) case nested(parentIcon: String, parentName: String, childName: String) }
步骤2:自定义递归层级列表视图
系统自带的层级列表无法直接获取父节点信息,因此需要手动实现递归视图,在遍历子分类时传递父分类的引用:
struct CategoryRow: View { let category: Category let parentCategory: Category? let onSelect: (Category, Category?) -> Void var body: some View { HStack { Image(systemName: category.icon) Text(category.name) } .onTapGesture { onSelect(category, parentCategory) } // 递归渲染子分类,添加缩进区分层级 if let subCategories = category.subCategory { ForEach(subCategories) { subCat in CategoryRow(category: subCat, parentCategory: category, onSelect: onSelect) .padding(.leading, 20) } } } }
步骤3:更新ContentView的状态与UI
替换原有状态变量,使用自定义递归视图,并在选中时更新对应状态,最后根据状态渲染「Selected Category」区域:
struct ContentView: View { @State private var items = Category.preview() @State private var selection: Selection? var body: some View { NavigationView { Form { Section { List { ForEach(items) { category in CategoryRow(category: category, parentCategory: nil, onSelect: { selectedCat, parent in if let parent = parent { // 点击子分类,保存父分类与子分类信息 selection = .nested(parentIcon: parent.icon, parentName: parent.name, childName: selectedCat.name) } else { // 点击独立分类,保存自身信息 selection = .standalone(icon: selectedCat.icon, name: selectedCat.name) } }) } } } header: { Text("Category List") } Section { HStack(spacing: 8) { switch selection { case .standalone(let icon, let name): Image(systemName: icon) Text(name) case .nested(let parentIcon, let parentName, let childName): Image(systemName: parentIcon) Text(parentName) Text(childName) .foregroundColor(.secondary) // 用颜色区分子分类 case nil: Text("请选择一个分类") .foregroundColor(.gray) } } } header: { Text("Selected Category") } } .navigationTitle("Category Selector") } } }
完整代码
import SwiftUI struct Category: Hashable, Identifiable { var name: String let icon: String var subCategory: [Category]? = nil let id = UUID() static func preview() -> [Category] { [Category(name: "Bills", icon: "lightbulb", subCategory: [Category(name: "Electricity", icon: "lightbulb"), Category(name: "Telephone", icon: "lightbulb")]), Category(name: "Transport", icon: "car", subCategory: [Category(name: "Taxi", icon: "car")]), Category(name: "Housing", icon: "house")] } } enum Selection { case standalone(icon: String, name: String) case nested(parentIcon: String, parentName: String, childName: String) } struct CategoryRow: View { let category: Category let parentCategory: Category? let onSelect: (Category, Category?) -> Void var body: some View { HStack { Image(systemName: category.icon) Text(category.name) } .onTapGesture { onSelect(category, parentCategory) } if let subCategories = category.subCategory { ForEach(subCategories) { subCat in CategoryRow(category: subCat, parentCategory: category, onSelect: onSelect) .padding(.leading, 20) } } } } struct ContentView: View { @State private var items = Category.preview() @State private var selection: Selection? var body: some View { NavigationView { Form { Section { List { ForEach(items) { category in CategoryRow(category: category, parentCategory: nil, onSelect: { selectedCat, parent in if let parent = parent { selection = .nested(parentIcon: parent.icon, parentName: parent.name, childName: selectedCat.name) } else { selection = .standalone(icon: selectedCat.icon, name: selectedCat.name) } }) } } } header: { Text("Category List") } Section { HStack(spacing: 8) { switch selection { case .standalone(let icon, let name): Image(systemName: icon) Text(name) case .nested(let parentIcon, let parentName, let childName): Image(systemName: parentIcon) Text(parentName) Text(childName) .foregroundColor(.secondary) case nil: Text("请选择一个分类") .foregroundColor(.gray) } } } header: { Text("Selected Category") } } .navigationTitle("Category Selector") } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
内容的提问来源于stack exchange,提问作者mzkn
相关产品推荐
相关产品推荐

