SwiftUI List with Toggle binding with model

I've got a model with a bool variable that i'll need to bind with a SwiftUI Toggle that is displayed in a List.


import SwiftUI

struct MyModel : Identifiable {
    var id: String
    var name: String
    var notify: Bool
}


import SwiftUI

struct ContentView : View {
    var myModels: [MyModel] = []
    
    var body: some View {
        NavigationView {
            List(myModels) { myModel in
                Toggle(isOn: myModel.notify) {
                    Text(myModel.name)
                }
            }
                .navigationBarTitle(Text("My Models"))
        }
    }
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView(tickets: MyModel.mockModels)
    }
}
#endif


When the user interacts with the toggle, i'll need the variable `notify` in the model to update.


The only example that i've looked at that implements a Toggle is the WorkingWithUIControls - LandmarkList but i can't seem to get it to work with an array of MyModels.


Any help would be much appreciated.

Answered by bfarah in 367386022

Okay, I found the solution thanks to Jumhynover at StackOverflow https://stackoverflow.com/a/56616127


This example compiles fine and works as expected.


struct MyModel {
    var id: String
    var name: String
    var notify: Bool
}

import SwiftUI

struct ContentView : View {
    @State var myModels: [MyModel] = [
        MyModel(id: "1", name: "First Model", notify: false),
        MyModel(id: "2", name: "Second Model", notify: true)
    ]
    
    var body: some View {
        NavigationView {
            List($myModels.identified(by: \.id.value)) { (myModel : Binding<MyModel>) in
                Toggle(isOn: myModel.notify) {
                    Text(myModel.value.name)
                }
            }
                .navigationBarTitle(Text("My Models"))
        }
    }
}

Has anyone attempted this pattern using SwiftUI? This can be used for a list of filters to be enabled. I might send Apple a code level support for this question if I don’t get a reply in a couple of days.

Accepted Answer

Okay, I found the solution thanks to Jumhynover at StackOverflow https://stackoverflow.com/a/56616127


This example compiles fine and works as expected.


struct MyModel {
    var id: String
    var name: String
    var notify: Bool
}

import SwiftUI

struct ContentView : View {
    @State var myModels: [MyModel] = [
        MyModel(id: "1", name: "First Model", notify: false),
        MyModel(id: "2", name: "Second Model", notify: true)
    ]
    
    var body: some View {
        NavigationView {
            List($myModels.identified(by: \.id.value)) { (myModel : Binding<MyModel>) in
                Toggle(isOn: myModel.notify) {
                    Text(myModel.value.name)
                }
            }
                .navigationBarTitle(Text("My Models"))
        }
    }
}

Edit: It appears my issue was just a matter of placing the $ binding in the right place


ForEach(0..<ops.count) { i in
     Toggle("Toggle", isOn: self.$ops[i].isSelected)
          .toggleStyle(OperatorToggle(op: self.ops[i]))
}




I have a similar setup with a few differences.

  • using ForEach instead of List
  • instead of an array of a struct, I have an array of structs conforming to a common protocol
  • the ForEach is iterating over a 0..<myArray.count


So far I've just tried to use your code but with my variable but my efforts so far have failed.


struct OpSelect: View {
    @State private var ops: [Operator] = [Add(), Subtract(), Multiply(), Divide()]

var body: some View {
        List($ops, id: \.self) { (op : Binding) in 
        //^ Cannot convert value of type '(Binding) -> _' to expected argument type '(_) -> _'
            Toggle(isOn: op.isSelected) {
            //^Cannot convert value of type 'Toggle' to closure result type '_'
                Text("My Toggle")
            }
        }
}


The code detailing the Operators


protocol Operator {
    var name: String { get }
    var symbol: String { get }
    var isSelected: Bool { get set }
//    var inverse: Operator { get }
    
    func operate(on a: Int, and b: Int) -> Int
//    func inverse(on a: Int, and b: Int) -> Int
}

extension Operator where Self: Hashable {} // https://stackoverflow.com/questions/50966560/make-a-protocol-conform-to-another-protocol

struct Add: Operator {
    let name = "add"
    let symbol = "plus"
    var isSelected = false
//    var inverse: Operator = Subtract()
    
    func operate(on a: Int, and b: Int) -> Int {
        return a + b
    }
}

struct Subtract: Operator {
    let name = "subtract"
    let symbol = "minus"
    var isSelected = false
//    var inverse: Operator = Add()
    
    func operate(on a: Int, and b: Int) -> Int {
        return a - b
    }
}

struct Multiply: Operator {
    let name = "multiply"
    let symbol = "multiply"
    var isSelected = false
//    var inverse: Operator = Divide()
    
    func operate(on a: Int, and b: Int) -> Int {
        return a * b
    }
}

struct Divide: Operator {
    let name = "divide"
    let symbol = "divide"
    var isSelected = false
//    var inverse: Operator = Multiply()
    
    func operate(on a: Int, and b: Int) -> Int {
        return a / b
    }
}
SwiftUI List with Toggle binding with model
 
 
Q