Search code examples
swiftmacosswiftuitoggle

SwiftUI Toggle how to distinguish changing value by UI action vs changing programatically


If i have a toggle, which updates its state from external async load but also by user intput, how can i differentiate those two? eg. to perform a special action on user action

    Group {
        Toggle(isOn: $on) {
            EmptyView()
        }
    }
    .onChange(of: on) { newValue in
        was "on" changed by user or onAppear async update?
    }
    .onAppear {
        async update on
    }

PS: this is mostly for macOS, and there the tapGesture on Toggle doesn't work


Solution

  • If you want a side effect for use the user actions, you can use a custom wrapper Binding:

    struct ContentView: View {
        @State private var on: Bool = false
        
        var userManagedOn: Binding<Bool> {
            .init {
                return on
            } set: { newValue in
                print("Side effect")
                on = newValue
            }
        }
        
        var body: some View {
            VStack {
                Group {
                    Toggle(isOn: userManagedOn) {
                        EmptyView()
                    }
                }
            }
            .padding()
            .onAppear {
                Task { @MainActor in
                    try? await Task.sleep(nanoseconds: NSEC_PER_SEC)
                    on.toggle()
                }
            }
        }
    }