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

SwiftUI中VStack+ForEach实现Toggle单选功能的问题

解决方案:实现SwiftUI单选Toggle功能

你的核心问题在于用单个Bool类型的@State来控制所有行的选中状态,导致所有ResultRow共享同一个绑定值,无法区分单独行的选中状态。要实现单选功能,需要改用选中项的唯一标识来追踪当前选中的行,具体修改如下:

步骤1:修改主视图的状态管理

将原来的单个Bool类型@State替换为可选类型,用来存储当前选中的countryCodeResult(假设CountryCodeResult是可哈希的自定义类型,比如包含唯一ID):

struct MainView: View {
    // 用可选类型记录当前选中的国家项,初始为nil(无选中)
    @State private var selectedCountry: CountryCodeResult?
    @ObservedObject var viewModel: YourViewModel // 替换为你的实际ViewModel类型

    public var body: some View {
        VStack(spacing: 0) {
            ForEach(viewModel.countryCodeResults, id: \.self) { country in
                ResultRow(
                    result: country,
                    isSelected: selectedCountry == country,
                    onSelect: {
                        // 点击时切换选中状态:已选中则取消,未选中则设为当前项
                        selectedCountry = selectedCountry == country ? nil : country
                    }
                )
            }
        }
    }
}

步骤2:重构ResultRow视图

移除直接绑定的@Binding Bool,改为接收外部传入的选中状态和点击回调,让视图只负责展示和转发交互:

public struct ResultRow: View {
    let result: CountryCodeResult
    let isSelected: Bool
    let onSelect: () -> Void

    public var body: some View {
        HStack(spacing: 10) {
            Text(result.name) // 替换为实际的文本字段,比如国家名称/代码
            Spacer()
            Toggle("", isOn: .constant(isSelected))
                .toggleStyle(.radioButton)
                .frame(maxWidth: 30)
                .disabled(true) // 禁用Toggle自身的交互,统一通过行点击触发
        }
        .lineLimit(1)
        .frame(maxWidth: .infinity, alignment: .leading)
        .onTapGesture(perform: onSelect)
    }
}

替代方案:使用计算型Binding

如果你希望保留ResultRow中的@Binding Bool,可以在主视图中传递一个计算型Binding,将选中状态与selectedCountry关联:

主视图中修改ForEach部分:

ForEach(viewModel.countryCodeResults, id: \.self) { country in
    ResultRow(
        result: country,
        selected: Binding(
            get: { selectedCountry == country },
            set: { isSelected in
                selectedCountry = isSelected ? country : nil
            }
        )
    )
}

ResultRow保持原有结构(仅修正View协议):

public struct ResultRow: View {
    let result: CountryCodeResult
    @Binding var selected: Bool

    public var body: some View {
        HStack(spacing: 10) {
            Text(result.name)
            Spacer()
            Toggle("", isOn: $selected)
                .toggleStyle(.radioButton)
                .frame(maxWidth: 30)
        }
        .lineLimit(1)
        .frame(maxWidth: .infinity, alignment: .leading)
        .onTapGesture {
            selected.toggle()
        }
    }
}

关键注意点

  1. 视图结构体必须遵守View协议(你的原代码中遗漏了: View声明)
  2. 用选中项的唯一标识(而非单个Bool)追踪状态,是实现单选的核心逻辑
  3. @State建议标记为private,符合SwiftUI状态管理的最佳实践

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 04:25:28