Search code examples
swiftswiftuitoggle

SwiftUI toggles not changing when clicked on


I'm trying to build a simple SwiftUI view that displays a number of toggles. While I can get everything to display ok, I cannot get the toggles to flip. Here's is a simplified code example:

import SwiftUI

class StoreableParam: Identifiable {
    let name: String
    var id: String { name }
    @State var isEnabled: Bool
    let toggleAction: ((Bool) -> Void)?
    
    init(name: String, isEnabled: Bool, toggleAction: ((Bool) -> Void)? = nil) {
        self.name = name
        self.isEnabled = isEnabled
        self.toggleAction = toggleAction
    }
}

class StoreableParamViewModel: ObservableObject {

    @Published var storeParams: [StoreableParam] = []
    init() {
                   
        let storedParam = StoreableParam(name: "Stored Param", isEnabled: false) { value in
            print("Value changed")
        }
        
        storeParams.append(storedParam)
    }
}

public struct UBIStoreDebugView: View {
    
    @StateObject var viewModel: StoreableParamViewModel
    public var body: some View {
        VStack {
            List {
                ForEach(viewModel.storeParams, id: \.id) { storeParam in
                    Toggle(storeParam.name, isOn: storeParam.$isEnabled)
                        .onChange(of: storeParam.isEnabled) {
                            storeParam.toggleAction?($0)
                        }
                }
            }
            
        }.navigationBarTitle("Toggle Example")
    }
}

Solution

  • As mentioned in the comments, there are a couple of things going on:

    • @State is only for use in a View
    • Your model should be a struct

    Then, you can get a Binding using the ForEach element binding syntax:

    struct StoreableParam: Identifiable {
        let name: String
        var id: String { name }
        var isEnabled: Bool
        let toggleAction: ((Bool) -> Void)?
    }
    
    class StoreableParamViewModel: ObservableObject {
    
        @Published var storeParams: [StoreableParam] = []
        init() {
                       
            let storedParam = StoreableParam(name: "Stored Param", isEnabled: false) { value in
                print("Value changed")
            }
            
            storeParams.append(storedParam)
        }
    }
    
    public struct UBIStoreDebugView: View {
        
        @StateObject var viewModel: StoreableParamViewModel
        
        public var body: some View {
            VStack {
                List {
                    ForEach($viewModel.storeParams, id: \.id) { $storeParam in
                        Toggle(storeParam.name, isOn: $storeParam.isEnabled)
                            .onChange(of: storeParam.isEnabled) {
                                storeParam.toggleAction?($0)
                            }
                    }
                }
                
            }
        }
    }