user15572366
user15572366

Reputation:

Action of UIButton doesn't work if the button is initialized with let and the addTarget in it in custom view, but it works in UIViewController

When I create a button like below in the custom view the action does not work:

private let usernameButton: UIButton = {
    let button = UIButton(type: .system)
    button.setTitleColor(.black, for: .normal)
    button.setTitle("someTitle", for: .normal)
    button.titleLabel?.font = .boldSystemFont(ofSize: 13)
    button.addTarget(self, action: #selector(didTapUsername), for: .touchUpInside)
    return button
}()

However, it works when I do the same in UIViewController somehow.

It always works if I make it lazy var but couldn't understand why it doesn't work if I make it let in custom view while It is working in UIViewController

Thanks for any comment.

Upvotes: 0

Views: 415

Answers (1)

Phil Dukhov
Phil Dukhov

Reputation: 87605

self inside that block actually is the block, not the view(you can see it using print(self)), probably because it's not yet initialised(same reason why you can't use self before super.init), that's why I usually add targets/delegates outside of the initialisation block. With lazy var self is already initialised, so no problem should be there.

I'm not sure why it works at all, seems kind of magic for me, and I wouldn't depend on it.

Anyway, it works fine with both custom ViewController and custom View in my case: I've added CustomView as subview to ViewController in the storyboard.

class ViewController: UIViewController {
    
    private let usernameButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitleColor(.black, for: .normal)
        button.setTitle("someTitle", for: .normal)
        button.titleLabel?.font = .boldSystemFont(ofSize: 13)
        button.addTarget(self, action: #selector(didTapUsername), for: .touchUpInside)
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.insertSubview(usernameButton, at: 0)
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        usernameButton.frame = view.bounds
    }
    
    @objc
    func didTapUsername() {
        print("ViewController", #function)
    }
}

class CustomView: UIView {
    private let usernameButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitleColor(.black, for: .normal)
        button.setTitle("someTitle", for: .normal)
        button.titleLabel?.font = .boldSystemFont(ofSize: 13)
        button.addTarget(self, action: #selector(didTapUsername), for: .touchUpInside)
        return button
    }()
    
    override func awakeFromNib() {
        super.awakeFromNib()
        addSubview(usernameButton)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        usernameButton.frame = bounds
    }
    
    @objc
    func didTapUsername() {
        print("CustomView", #function)
    }
}

This shouldn't do differently for UIViewController/UIView, check out your logic

Upvotes: 1

Related Questions