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

SwiftUI互斥Toggle实现问题求助:点击Toggle致程序崩溃

Fixing Mutually Exclusive Toggles in SwiftUI (With No Crashes!)

Hey there! Let's break down why your current implementation is crashing and get those mutually exclusive Toggles working exactly how you want them—with always at least one selected, and no crashes when clicking an already active Toggle.

The Root Cause of the Crash

Your current didSet logic is causing an infinite loop. For example, when BY2_allAges is set to true, you immediately set BY2_allAges = true again inside its own didSet. This triggers the didSet over and over until the app crashes. Plus, managing three separate booleans is error-prone and hard to maintain.

Solution 1: Use an Enum (The Cleanest Approach)

Enums are perfect for mutually exclusive states—they naturally enforce that only one option can be selected at a time. Here's how to refactor your code:

Step 1: Define an Enum for Age Groups

enum BY2AgeGroup: String, CaseIterable {
    case allAges = "Alle aldre"
    case fiveYear = "5-års aldersgrupper"
    case tenYear = "10-års aldersgrupper"
}

Step 2: Update Your Data Model

Replace the three boolean properties with a single published enum property:

final class UserData: ObservableObject {
    @Published var DSTSelections: DSTSelection = DSTSelection()
}

final class DSTSelection: ObservableObject {
    // Default to the 10-year group as you had before
    @Published var selectedBY2AgeGroup: BY2AgeGroup = .tenYear
}

Step 3: Update the SwiftUI View

Use a ForEach to render the toggles, and a custom binding to link each toggle to the selected enum value:

struct DSTScrollView: View {
    @EnvironmentObject private var userData: UserData
    
    var body: some View {
        ScrollView(.vertical, showsIndicators: true){
            VStack{
                Text("Befolkning og Valg Tabeller")
                    .font(.subheadline)
                    .multilineTextAlignment(.center)
                
                HStack {
                    VStack (alignment: .leading){
                        Text("Folketal (BY2)")
                        // Loop through all age group options
                        ForEach(BY2AgeGroup.allCases, id: \.self) { group in
                            Toggle(
                                group.rawValue,
                                isOn: Binding(
                                    get: { userData.DSTSelections.selectedBY2AgeGroup == group },
                                    set: { isSelected in
                                        // Only update if the toggle is being turned on
                                        if isSelected {
                                            userData.DSTSelections.selectedBY2AgeGroup = group
                                        }
                                    }
                                )
                            )
                        }
                    }
                    Spacer()
                }
                .padding(.leading, 8)
            }
        }
        .frame(maxWidth: 580, minHeight: 300)
        .border(Color.black)
    }
}

This approach guarantees only one toggle is selected at all times, eliminates crash risks, and makes adding new age groups later trivial—just add a case to the enum!

Solution 2: Fix the Boolean Logic (If You Prefer to Keep Booleans)

If you want to stick with your original boolean setup, you need to fix the didSet logic to avoid infinite loops and enforce the "at least one selected" rule:

final class DSTSelection: ObservableObject {
    @Published var BY2_allAges: Bool = false {
        didSet {
            // Only deselect others if this toggle was turned on
            if BY2_allAges {
                BY2_5Year = false
                BY2_10Year = false
            } else {
                // Prevent deselecting if it would leave no options selected
                if !BY2_5Year && !BY2_10Year {
                    BY2_allAges = true
                }
            }
        }
    }
    
    @Published var BY2_5Year: Bool = false {
        didSet {
            if BY2_5Year {
                BY2_allAges = false
                BY2_10Year = false
            } else {
                if !BY2_allAges && !BY2_10Year {
                    BY2_5Year = true
                }
            }
        }
    }
    
    @Published var BY2_10Year: Bool = true {
        didSet {
            if BY2_10Year {
                BY2_allAges = false
                BY2_5Year = false
            } else {
                if !BY2_allAges && !BY2_5Year {
                    BY2_10Year = true
                }
            }
        }
    }
}

Key fixes here:

  • Removed redundant lines like BY2_allAges = true that caused infinite loops
  • Added checks to prevent deselecting the last active toggle, ensuring at least one is always selected
  • Only modify other toggles when the current one is being turned on

Final Notes

The enum approach is strongly recommended because it's more maintainable, less error-prone, and aligns perfectly with your mutually exclusive requirement.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 16:32:37