Search code examples
iosswiftuibuttonuistackview

Action not being called when button is tapped in a stack view


I have a custom view that includes a stack view. Inside the stack view I have a label and a button.

I created my stack view, label and button in the following way and added them to the parent view.

class HomeView: UIView {

override init(frame: CGRect) {
    super.init(frame: frame)
    translatesAutoresizingMaskIntoConstraints = false

    addSubview(stackView)
    stackView.addArrangedSubview(haveAccount)
    stackView.addArrangedSubview(signin)
    stackView.setCustomSpacing(4.0, after: haveAccount)
}

let stackView: UIStackView = {
    let stack = UIStackView()
    stack.translatesAutoresizingMaskIntoConstraints = false
    stack.distribution = .fillProportionally
    stack.alignment = .fill
    stack.isUserInteractionEnabled = false
    return stack
}()

let haveAccount: UILabel = {
    let label = UILabel()
    ...
    return label
}()

let signin: UIButton = {
    let button = UIButton()
    button.translatesAutoresizingMaskIntoConstraints = false
    button.setTitle("Sign in", for: .normal)
    button.titleLabel?.font = UIFont(name: "Avenir", size: 14)
    button.setTitleColor(UIColor.white, for: .normal)
    button.addTarget(self, action: #selector(HomeController.loginClicked(_:)), for: .touchUpInside)
    return button
}()

}

In my view controller I add the view to the controller's base view and set the constraints. I also create the method that should be called when the signin button is tapped.

override func viewDidLoad() {
    super.viewDidLoad()

    homeView = HomeView()
    homeView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(homeView)
    homeView.fullscreenView(parentView: view)
}

@objc func loginClicked(_ sender: UIButton) {        
    print("sign in button pressed")
}

When I press the button the loginClicked method is not called. Now I did tried moving the loginClicked method to the custom view and changing the addTarget accordingly and loginClicked method is called. This being said I know the button is clickable but I don't think the target for the button action is correct and that is why the loginClicked method in the view controller is not being called.


Solution

  • You can use Protocol/Delegation

    //1. Create a protocol
    
    protocol HomeViewDelegate{
        func loginButtonClicked(sender: UIButton)
    }
    
    class HomeView: UIView {
    
        //2. Create a delegate
        var delegate: HomeViewDelegate?
    
        let stackView: UIStackView = {
            let stack = UIStackView()
            stack.translatesAutoresizingMaskIntoConstraints = false
            stack.distribution = .fillProportionally
            stack.alignment = .fill
            stack.isUserInteractionEnabled = false
            return stack
        }()
    
        let haveAccount: UILabel = {
            let label = UILabel()
    
            return label
        }()
    
        let signin: UIButton = {
            let button = UIButton()
            button.translatesAutoresizingMaskIntoConstraints = false
            button.setTitle("Sign in", for: .normal)
            button.titleLabel?.font = UIFont(name: "Avenir", size: 14)
            button.setTitleColor(UIColor.white, for: .normal)
            button.addTarget(self, action: #selector(loginClicked(sender:)), for: .touchUpInside)
            button.backgroundColor = .red
            return button
        }()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            translatesAutoresizingMaskIntoConstraints = false
    
            addSubview(stackView)
            stackView.addArrangedSubview(haveAccount)
            stackView.addArrangedSubview(signin)
            stackView.setCustomSpacing(4.0, after: haveAccount)
    
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
       //3. Call your protocol method via delegate
       @objc func loginClicked(sender: UIButton) {
            if let delegate = delegate{
                delegate.loginButtonClicked(sender: sender)
            }
        }
    
    }
    

    In You Caller ViewController create an extension

    extension ViewController: HomeViewDelegate{
        func loginButtonClicked(sender: UIButton) {
            print("login Button Clicked")
        }
    }