iosswiftuikitaddtarget

The default parameters of the Swift addtarget method do not take effect


This is my code:

import UIKit

class ViewController: UIViewController {
     override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        let btn = UIButton.init(frame: CGRect(x: 100, y: 100, width: 100, height: 100));
        btn.backgroundColor = UIColor.blue
        btn.addTarget(self, action: #selector(testAction(_:animated:)), for: .touchDown)
        view.addSubview(btn)
    }
    @objc func testAction(_ sender: UIButton,animated: Bool = true){
        if sender.backgroundColor == UIColor.red {
            sender.backgroundColor = UIColor.blue
        }else{
            sender.backgroundColor = UIColor.red
        }
        print("animated == \(animated)")
    }
}

Xcode screenshot

As shown in the picture, a button is added to the test project. The method has default parameters, but the animated parameter is printed as false. Can anyone tell me why? I would be very grateful.

Is this a system bug? I have tested this in many scenarios.


Solution

  • The UIButton calls testAction by performing the selector you passed to action: on the object you passed to target:, using a method like performSelector:withObject:. This is inherently an Objective-C thing, and Swift parameters' default values just don't work in Objective-C.

    In Objective-C, func testAction(animated: Bool = true) is imported as if it doesn't have an optional parameter. The header looks like this:

    - (void)testActionWithAnimated:(BOOL)animated;
    

    UIButton expects its action to be a method that takes a parameter representing the "sender" of the event, and it would be pass itself (an UIButton instance) as the argument.

    So what happens here is that the animated parameter gets passed a pointer to an instance of UIButton, which is forcefully interpreted as a BOOL (because Objective-C has a rather weak type system). The result of that happens to be false.

    If you declare testAction with two parameters,

    @objc func testAction(_ sender: UIButton, animated: Bool = true)
    

    UIButton would pass a UIEvent instance to the second parameter, and this again gets interpreted as a boolean, and the result happens to be false.