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

SwiftUI使用DisclosureGroup实现可展开列表时设置isExpanded=false无法自动收起问题求助

问题原因
  • 状态隔离失效:你将isExpanded声明为SingleSelectionRow内部的@State属性,每个行实例的该变量完全独立,外部(包括其他行、父视图)都无法直接访问修改。你在无children的行点击时设置的isExpanded = false,仅修改了当前无children行的独立状态,完全无法触达正在展开的有children行的isExpanded值,这是收起逻辑不生效的核心原因。
  • 点击逻辑冲突:你给已经绑定$isExpanded的DisclosureGroup额外添加了.onTapGesture手动切换展开状态。而DisclosureGroup本身点击label时会自动修改绑定的展开属性,两套逻辑叠加会导致展开状态被重复切换,出现点击无响应的异常。
  • 子项点击未触发收起:你在DisclosureGroup的content区域的子项按钮action中仅调用了外部传入的self.action,没有主动将当前行的isExpanded设为false,因此点击当前展开列表的子选项时,列表不会自动收起。
修复思路
  • 将isExpanded的状态从行内部上移到父视图统一管理,实现跨行状态控制。父视图可记录当前展开的行唯一标识,当点击任意其他行/子项时直接清空展开标识,即可自动收起所有展开列表。
  • 移除DisclosureGroup上多余的.onTapGesture,依赖组件本身的绑定属性处理点击切换,避免逻辑冲突。
  • 在DisclosureGroup的content子项按钮的action中添加收起逻辑,点击子项时主动将展开状态设为false。
修正后代码示例

行组件修改

struct SingleSelectionRow: View {
    // 移除内部@State的isExpanded,替换为绑定属性,由父视图统一控制
    @Binding var isExpanded: Bool
    @Binding var selectSingle: Int
    @Binding var selectedFilter: String?
    var title: String
    var isSelected: Bool
    var childrenList: [FilterChildren]
    @State var filterDetail: SortAndFilterData
    @State var singleSelectedFilters: [String]
    var action: () -> Void

    var body: some View {
        if selectSingle == 1 {
            // 无children的普通选项行
            if childrenList.isEmpty {
                Button(action: {
                    self.action()
                    // 清空当前行的展开状态,父视图会同步收起其他展开列表
                    isExpanded = false
                }) {
                    Text(self.title.uppercased())
                        .font(.custom("DM Sans",size:15))
                        .foregroundColor(isSelected ? Color(red: 10 / 255, green: 207 / 255, blue: 131 / 255) : Color.black)
                }
            }

            // 有children的可展开行
            if !childrenList.isEmpty {
                DisclosureGroup(
                    isExpanded: $isExpanded,
                    content: {
                        ForEach(childrenList, id: \.id) { item in
                            Button(action: {
                                self.action()
                                // 点击子项后主动收起当前展开的列表
                                isExpanded = false
                            }) {
                                Text(item.name ?? "")
                                    .font(.custom("DM Sans",size:15))
                                    .frame(maxWidth: .infinity, alignment: .leading)
                            }
                        }
                    },
                    label: {
                        Text(self.title.uppercased())
                            .foregroundColor(isSelected ? Color(red: 10 / 255, green: 207 / 255, blue: 131 / 255) : Color.black)
                    }
                )
                // 移除多余的.onTapGesture,使用DisclosureGroup原生点击逻辑
            }
        }
    }
}

父视图实现示例(统一管理展开状态)

struct FilterListView: View {
    // 父视图维护当前展开的行唯一标识,nil表示无展开项
    @State private var expandedRowID: String?
    @Binding var selectedFilter: String?

    var body: some View {
        ForEach(filterRowList) { row in
            SingleSelectionRow(
                // 绑定当前行的展开状态:仅当当前行ID与expandedRowID一致时为展开
                isExpanded: Binding(
                    get: { expandedRowID == row.id },
                    set: { newValue in
                        // 展开时记录当前行ID,收起时清空标识
                        expandedRowID = newValue ? row.id : nil
                    }
                ),
                selectSingle: .constant(1),
                selectedFilter: $selectedFilter,
                title: row.title,
                isSelected: row.isSelected,
                childrenList: row.childrenList,
                filterDetail: row.filterDetail,
                singleSelectedFilters: row.singleSelectedFilters,
                action: { /* 原有选中回调逻辑 */ }
            )
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.29 17:54:04