SwiftUI中如何让List将DisclosureGroup整体视为单个列表项?
SwiftUI List中DisclosureGroup的单元格识别问题
在使用SwiftUI的List组件时,若在列表项中嵌入DisclosureGroup,List会将DisclosureGroup的展开内容识别为独立的列表单元格,这会导致onMove和onDelete功能出现异常——操作时会误选中展开内容而非整个组,引发无意义的未定义行为。我们需要让整个DisclosureGroup组被视为单个列表元素。
示例代码
struct ContentView: View { @State var items: [Int] = [0, 1, 2, 3] var body: some View { List { ForEach($items, id: \.self) { $item in DisclosureGroupView() } .onDelete { indexSet in items.remove(atOffsets: indexSet) } .onMove { indices, newOffset in items.move(fromOffsets: indices, toOffset: newOffset) } } } } struct DisclosureGroupView: View { var body: some View { DisclosureGroup { VStack(spacing: 8) { Text("Test text") Text("Test text") Text("Test text") } } label: { HStack { Text("Tap to Expand") Spacer() Image(systemName: "ellipsis") } } } }
问题演示

可以看到,删除/重排操作会误作用于DisclosureGroup的展开内容,而非整个组,这不符合预期。
解决方案
方法一:使用.disclosureGroupStyle(.header)调整布局逻辑
通过指定DisclosureGroup的样式为.header,让展开内容隶属于当前单元格,而非生成新的列表项,同时配合listRowInsets消除额外间距:
struct DisclosureGroupView: View { var body: some View { DisclosureGroup { VStack(spacing: 8) { Text("Test text") Text("Test text") Text("Test text") } .padding(.leading) // 手动模拟默认展开内容的缩进效果 } label: { HStack { Text("Tap to Expand") Spacer() Image(systemName: "ellipsis") } } .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) .disclosureGroupStyle(.header) } }
方法二:用容器视图包裹DisclosureGroup
将DisclosureGroup放入VStack或Group中,强制List将整个结构识别为单个单元格:
struct DisclosureGroupView: View { var body: some View { VStack(spacing: 0) { DisclosureGroup { VStack(spacing: 8) { Text("Test text") Text("Test text") Text("Test text") } .padding(.leading) } label: { HStack { Text("Tap to Expand") Spacer() Image(systemName: "ellipsis") } } } .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) } }
关键说明
listRowInsets用于消除容器与List单元格边缘的额外空白;- 手动添加
.padding(.leading)是为了还原默认展开内容的缩进视觉效果,可根据需求调整。
内容的提问来源于stack exchange,提问作者Nicolas Gimelli
相关产品推荐
相关产品推荐

