3
votes

I'm trying to set the transition animation options for the Switch I created on the screen.

So I used the Instance Method below.

func setOn(_ on: Bool, animated: Bool)

I saw description in the apple dev documentation that the second param decide the difference in animation, but in reality both true/false worked with animation included. https://developer.apple.com/documentation/uikit/uiswitch/1623686-seton

Emulator demonstration gif image

I tested it with a real phone, but it was the same issue. Environment I checked:

  • macOS Catalina 10.15.7
  • Xcode 12.1
  • Simulator: iOS 14.2, iOS 13.7, iOS 13.1
  • iPhone XR (iOS 14.2)

Here is my code : (The only difference is the value of "animated: Bool")

    self.switch1 = UISwitch()
    self.switch1.frame = CGRect(x: 120, y: 150, width: 50, height: 30)
    self.switch1.setOn(true, animated: true)
    self.view.addSubview(switch1)

    self.switch2 = UISwitch()
    self.switch2.frame = CGRect(x: 120, y: 250, width: 50, height: 30)
    self.switch2.setOn(true, animated: false)
    self.view.addSubview(switch2)

What's wrong and how can I turn off the animation options?

2
First, you are calling setOn(...) before you've added the switch to the view - so animated or not won't matter... the first time you see the switch on the screen it will be in the "On" state. Second, .setOn(...), animated or not, is a function you could call based on some other action - such as changing the state of the switch1 when the user taps on switch2. Are you trying to disable the animation when the switch is tapped?DonMag
@DonMag yes, and I was mistaken that SetOn() set the animation option when tapped. What I newly understand is , It was an animation setting when changing a switch state programmatically.mi kim

2 Answers

1
votes

This animation is default UISwitch feedback on user interaction, you cannot disable that animation from direct interaction, but we can prevent it overriding control and use own interaction (and still keep possibility to animate it if needed programmatically).

Tested with Xcode 12.1 / iOS 14.1

demo

Here is possible approach:

class MySwitch: UISwitch {
    override init(frame: CGRect) {
        super.init(frame: frame)

        let view = UIView()
        view.isUserInteractionEnabled = true

        self.addSubview(view)
        view.translatesAutoresizingMaskIntoConstraints = false
        view.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
        view.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
        view.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
        view.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true

        let gr = UITapGestureRecognizer(target: self, action: #selector(toggle(_ :)))
        view.addGestureRecognizer(gr)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    @objc func toggle(_ sender: Any?) {
        self.setOn(!self.isOn, animated: false)
    }
}
1
votes

You have to force call the 'setOn' callback every time you press the switch. However, in order for the tapGestureRecognizer to respond to your callback, you have to eliminate all other default gestureRecognizers (found on the subviews).

private func initView() {
    uiSwitch = UISwitch(frame: .zero)
    
    // remove all underlaying subviews gesture recognizers
    if let subviews = uiSwitch?.subviews {
        for subview in subviews {
            let gestures = subview.gestureRecognizers
            gestures?.forEach { subview.removeGestureRecognizer($0) }
        }
    }
    
    // add new preferred gesture recognizer
    uiSwitch?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(switchHasBeenPressed)))
}

@objc
private func switchHasBeenPressed() {
    guard let uiSwitch = uiSwitch else { return }
    uiSwitch.setOn(!uiSwitch.isOn, animated: false)
}