Home > Back-end >  How to prevent programmatical toggle of Switch
How to prevent programmatical toggle of Switch

Time:10-24

I need to prevent UISwitch (or Toggle for SwiftUI) programmatical state change. I just want the state to change with user interaction.

The below code working on debug mode.

SwiftUIView

import SwiftUI

struct SwiftUIView: View {

     @State var myState = false

     @State var myProvider = MyProvider()

     var body: some View {
         Toggle("Let me in", isOn: $myState.onChange(myProvider.checkStateAction))
                .textFieldStyle(.roundedBorder)
                .padding(.trailing).padding(.leading)
          }
     }
}

@available(iOS 13.0, *)
extension Binding {
func onChange(_ handler: @escaping (Value) -> Void) -> Binding<Value> 
{        
    Binding(  
        get: { self.wrappedValue },
        set: { newValue in
           self.wrappedValue = newValue
           handler(newValue)
        }
    )
}
}

MyProvider

import Foundation

public final class MyProvider: NSObject {

     public var onCheckBoxStateChanged: ((_ state: Bool) -> Void)?

     private(set) var state: Bool = false

     internal var stateChanger: Bool = false {
        didSet {
            state = self.stateChanger
        }
     }

     public override init() {
         super.init()
     }

     @objc public final func checkStateAction(to value: Bool) {
         let symbols = Thread.callStackSymbols
         let str: String = symbols[3]
         if str.contains("sendAction") == false && 
         str.contains("SwiftUI7Binding") == false {
             print("It's done")
         }
         state.toggle()
         onCheckBoxStateChanged?(state)
     }
}

Please tell me how can I do that? Or you can show me another way :)

Thanks..

CodePudding user response:

What you can do is disable the Toggle's interaction and overlay it with a Button so that when the user presses on the Toggle, the button is tapped and you call whatever function you want:

struct SwiftUIView: View {
    @State var myState = false
    @State var myProvider = MyProvider()
    var body: some View {
        Toggle("Let me in", isOn: $myState)
            .allowsHitTesting(false)
            .textFieldStyle(.roundedBorder)
            .padding(.trailing).padding(.leading)
            .overlay {
                Button(action: {
                    myProvider.checkStateAction()
                }) {
                    Color.clear
                }
            }
    }
}

CodePudding user response:

To disable UISwitch:

switch.isEnabled = false

To put UISwitch off:

switch.isOn = false

To put UISwitch on:

switch.isOn = true

To hide UISwitch:

switch.isHidden = true

CodePudding user response:

struct SwiftUIView: View {

  @State var myState = false

  var sourceOfTruthValue: Bool {
    return sourceOfTruth.wrappedValue
  }

  private var sourceOfTruth: Binding<Bool> {
    var value = false
    return Binding {
      return value
    } set: { newValue in
      value = newValue
      myState = newValue
    }
  }

  var body: some View {
    Toggle("toggle1", isOn: sourceOfTruth)
      .textFieldStyle(.roundedBorder)
      .padding(.trailing).padding(.leading)

    Toggle("toggle2", isOn: $myState)
  }
}

Added 2 toggles to experiment with. When toggle1 is changed only then sourceOfTruth is updated. When toggle2 is updated, sourceOfTruth is not updated.

Changing myState won't update the sourceOfTruth.

You can always read the value of sourceOfTruth from sourceOfTruthValue.

You can run the above in a playground:

import PlaygroundSupport
import SwiftUI

struct ContentView: View {
  var body: some View {
    SwiftUIView()
  }
}

PlaygroundPage.current.setLiveView(ContentView())
  • Related