1

The following code is located inside a subclass of UIView

I am setting up a cancelButton inside a closure:

private var cancelButtonClosure: UIButton = {
    ...
    button.addTarget(self, action: #selector(cancel(_:)), for: .touchUpInside)
    ...
}()

And at first I instantiated the button inside a function like so:

func showConfirmationView(...) {
    ...
    let cancelButton = self.cancelButtonClosure
    ...
    addSubview(cancelButton)
    ...
}

However this resulted in the cancel function not being called at all (even though the layout was right and the button was highlighting)

So I made these change:

  • Removed the addTarget part from the cancelButtonClosure
  • Added the addTarget part inside the showConfirmationView function

So it looked like that:

func showConfirmationView(...) {
    ...
    let cancelButton = self.cancelButtonClosure
    cancelButton.addTarget(self, action: #selector(cancel(_:)), for: .touchUpInside)
    ...
    addSubview(cancelButton)
    ...
}

It worked: the cancel function was called; but I don't know why. I'm really curious to know why what I did before did not work. Thanks for your insights!

Stormsyders
  • 285
  • 2
  • 11
  • Your `cancelButtonClosure` isn't really a closure but its result (an instance of `UIButton`) since you immediately execute it with `()`. That also means that your line `let cancelButton = self.cancelButtonClosure` does not **instantiate** the button (since it already is) but assigns it to the `cancelButton` variable. – André Slotta Jun 27 '18 at 11:40
  • Few remarks: 1. Don't name your button as `cancelButtonClosure`. Because this is a result of closure which you described in {}. So name it as `cancelButton` and it will be your already initialized button. 2. `let cancelButton = self.cancelButtonClosure` not initializes button. In this case you set reference on `self.cancelButtonClosure` into `cancelButton` variable and thats all. Use only `self.cancelButton` or `cancelButton` as I wrote previously. About your question. There are a lot of reasons why it can't works. Starts from views hierarchy. Maybe another view placed under the button. – RomanHouse Jun 27 '18 at 12:15
  • I'm also experiencing this. Also written up here: https://stackoverflow.com/a/47999822/1265393 – pkamb Apr 13 '21 at 05:13

1 Answers1

0

Check your implementation because a setup like this works as expected:

private var cancelButton: UIButton = {
    let btn = UIButton(type: .system)
    btn.setTitle("Cancel", for: .normal)
    btn.addTarget(self, action: #selector(cancelSomething(_:)), for: .touchUpInside)
    return btn
}()

@objc func cancelSomething(_ sender: UIButton) {
    print("Something has to be cancelled")
}

override func viewDidLoad() {
    super.viewDidLoad()
    showConfirmationView()
}

func showConfirmationView() {
    cancelButton.sizeToFit()
    cancelButton.center = view.center

    view.addSubview(cancelButton)
}
André Slotta
  • 13,774
  • 2
  • 22
  • 34
  • i know this code is working but if you print self in the cancel block, it will print (function), why is that and does that mean self in the block is representing the block itself? if that is true than how can we give the block itself as a param in target parameter? – Yasir Ali Jun 27 '18 at 12:20
  • Yes. `self` is referencing the closure itself. Actually it does not make any sense in this case (and could be replaced with `nil`) since the closure won't be there anymore after it got executed. So the `target` parameter is actually `nil`. Nevertheless the action gets fired because `When the target object of a control is nil, UIKit starts from the target object and traverses the responder chain until it finds an object that implements the appropriate action method.`. That means it will find the method implemented in the viewcontroller and executes it. – André Slotta Jun 27 '18 at 13:09