Search code examples
iosswiftnslayoutconstraint

Layout constraints not working with view.frame.size.height


I'm creating my UI programmatically and I want to make my UITextfield topAnchor to be based off the height of its containing UIView but it's not working.

My UITextfield is inside a UIView and I want its topAnchor to be 0.15 the height of the UIView as such - containerView.frame.size.height * 0.15 but it seems the value is not picking up. All this is done in a UIView subclass and called in the ViewController

        let containerView: UIView
        containerView.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(containerView)

        NSLayoutConstraint.activate([
            containerView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
            containerView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.9),
            containerView.topAnchor.constraint(equalTo: stepTitleLabel.bottomAnchor, constant: 50),
            containerView.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: 0.65)
            ])


        let userTextField: UITextField
        userTextField?.tag = 1
        userTextField?.translatesAutoresizingMaskIntoConstraints = false
        containerView.addSubview(userTextField)

        userTextField.setNeedsLayout()
        print("containerView.frame.size.height * 0.15:\(containerView.frame.size.height * 0.15)")
        NSLayoutConstraint.activate([
            (userTextField.centerXAnchor.constraint(equalTo: containerView.centerXAnchor))!,
            (userTextField.widthAnchor.constraint(equalTo: containerView.widthAnchor, multiplier: 0.8))!,
            userTextField.topAnchor.constraint(equalTo: containerView.topAnchor, constant: containerView.frame.size.height * 0.15),
            userTextField.heightAnchor.constraint(equalToConstant: 40)
            ])

I expect the topAnchor of my UITextField to appear similar across all devices at 0.15 the height of it container view but what I'm getting is 0


Solution

  • Don't mix NSLayoutConstraints and frames. The frames are static and don't update when you use them for the constant in a constraint. So when Auto Layout uses the other constraints to update the height of containerView, your constraint that looks at containerView's frame height will be based upon the height of the containerView at the time of the constraint's creation (which is likely 0) and not at the time Auto Layout uses the constraints to establish the height of containerView.

    Instead, you want to base the top of userTextField on the bottom of containerView with a multiplier. Unfortunately, you can't do that with layout anchors, and you'll have to use NSLayoutContraint to create it:

    Replace:

    userTextField.topAnchor.constraint(equalTo: containerView.topAnchor,
        constant: containerView.frame.size.height * 0.15)
    

    with:

    NSLayoutConstraint(item: userTextField, attribute: .top, relatedBy: .equal,
        toItem: containerView, attribute: .bottom, multiplier: 0.15, constant: 0)