简体   繁体   中英

Programmatic UIButton action not being called

I've created a class that will create/manage several buttons and labels, and add them to a view, but I can't get the actions from the buttons to trigger.

Here's a simple version of the code without a 'MakerClass' that works fine (code is in the main ViewController):

@objc func pressed(_ sender: UIButton!) {
    print("pressed")
}
    override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.


    let backgroundButton = UIButton()
    backgroundButton.backgroundColor = UIColor.green
    backgroundButton.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
    self.view.addSubview(backgroundButton)

    backgroundButton.addTarget(self, action: #selector(pressed(_:)), for: .touchUpInside)

}

And here is a version where I put the creation of the button into a different Class

ViewController

    override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.


    MakerClass(intoView: self.view)                
}

MakerClass

class MakerClass {


    var backgroundButton: UIButton = UIButton()


    @objc func iwastouched(_ sender: UIButton!) {
        print("touched")
    }

    init(intoView: UIView){

        backgroundButton.backgroundColor = UIColor.red
        backgroundButton.frame = CGRect(x: 0, y: 200, width: 100, height: 100)
        intoView.addSubview(backgroundButton)
        backgroundButton.addTarget(self, action: #selector(iwastouched(_:)), for: .touchUpInside)

    }


}

The project I've been using to play around with this can be downloaded from here

I may be going about this the wrong way, as I am new to iOS programming

The problem is that as you're not retaining MakerClass , the target for the button is set to NSNull()

You can see this by adding…

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    let buttons = view.subviews as! [UIButton]
    for button in buttons {

        print(button)
        print("Target :", button.allTargets.first!)
    }
}
 <UIButton: 0x7ff89fd04110; frame = (0 0; 100 100); opaque = NO; layer = <CALayer: 0x60000257bac0>> Target : <TestProgrammaticButton.ViewController: 0x7ff89ff08050> <UIButton: 0x7ff89fd05020; frame = (0 200; 100 100); opaque = NO; layer = <CALayer: 0x60000257bb40>> Target : <null>

If you want the button to fire an action method in your view controller, you could do…

class MakerClass {
    init(intoView: UIView, with viewController: ViewController) {
        // configure
        backgroundButton.addTarget(viewController, action: #selector(iwastouched(_:)), for: .touchUpInside)
    }
}

But that ties your MakerClass to that specific ViewController class.

Instead, your MakerClass could return the formatted button and then set the target/action in the view controller.


Update

Per @RakeshaShastri's comment below, as the target is set to NSNull() , the app will go up the responder chain looking for an UIResponder (generally a UIView or UIViewController ) with the correct func signature. The responder chain in your case is…

backgroundButton -> ViewController.view -> ViewController -> UIWindow

You can test this out by also adding

@objc func iwastouched(_ sender: UIButton!) {
    print("touched")
}

to your ViewController class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM