IMKt
IMKt

Reputation: 63

How do I set a group of toggles to be mutually exclusive in swift

@State private var isOn1 = false
@State private var isOn2 = false

var body: some View {
    ScrollView{
        Toggle("switch1",isOn:$isOn1)
        Toggle("switch2",isOn:$isOn2)
    }

Here is what I have so far. Is there a way for me to add an if statement when activating a toggle that all the other toggles will be off. Or is there anyway to change the states of the toggles when activating one. Any answers are appreciated thank you!

Upvotes: 6

Views: 2680

Answers (3)

Abhi
Abhi

Reputation: 21

Here is a simpler solution and more practical example. Keep track of the active selection and create a binding that checks and sets the active selection

struct Item: Identifiable, Hashable {
  var name: String
  let id: UUID = .init()
}

struct ContentView: View {
  @State var items: [Item] = [Item(name: "Item 1"),
                              Item(name: "Item 2"),
                              Item(name: "Item 3")]

  @State var activeItem: Item?

  var body: some View {
    List(items) { item in
      HStack {
        Text(item.name)
        let isOn = Binding<Bool>(get: { activeItem == item },
                                 set: { _ in activeItem = item })
        Toggle("", isOn: isOn)
      }
    }
    .onAppear {
      activeItem = items[0]
    }
  }
}

Upvotes: 0

Asperi
Asperi

Reputation: 257493

Here is some alternate approach, which does not require to hardcode interdependently all bindings, but instead use shared storage of bools:

demo

struct DemoExclusiveToggles: View {
    @State var flags = Array(repeating: false, count: 9)
    var body: some View {
        ScrollView {
            ForEach(flags.indices) { i in
                ToggleItem(storage: self.$flags, tag: i, label: "Switch \(i+1)")
                    .padding(.horizontal)
            }
        }
    }
}

struct ToggleItem: View {
    @Binding var storage: [Bool]
    var tag: Int
    var label: String = ""

    var body: some View {
        let isOn = Binding (get: { self.storage[self.tag] },
            set: { value in
                withAnimation {
                    self.storage = self.storage.enumerated().map { $0.0 == self.tag }
                }
            })
        return Toggle(label, isOn: isOn)
    }
}

Upvotes: 8

David Pasztor
David Pasztor

Reputation: 54706

You can define isOn2 as a Binding. You can create a Binding by passing in a closure for its getter and another one for its setter. For your isOn2 Binding you'll simply need to return the negated value of isOn1 and in its setter, you'll set isOn1 to the negated value passed in to the setter.

struct ToggleView: View {
    @State private var isOn1 = false

    var body: some View {
        let isOn2 = Binding<Bool>(
            get: { !self.isOn1 },
            set: { self.isOn1 = !$0 }
        )
        return ScrollView{
            Toggle("switch1",isOn: $isOn1)
            Toggle("switch2",isOn: isOn2)
        }
    }
}

Upvotes: 2

Related Questions