Search code examples
iosswiftconstraintsnslayoutconstraintappdelegate

Unable to activate constraint with anchors while adding a textfield


A while ago I started creating my screens via the storyboard.

Recently I have been convinced doing it programmatically.

Currently I am running into the following error:

Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors and because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.'

import UIKit

class ViewController: UIViewController {

   var emailTextField: UITextField {

        let tf = UITextField()
        tf.placeholder = "email";
        tf.backgroundColor = UIColor(white: 0, alpha: 0.03)
        tf.borderStyle = .roundedRect
        tf.font = UIFont.systemFont(ofSize: 14)

        return tf
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white

        view.addSubview(emailTextField)

        emailTextField.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 40, paddingLeft: 20, paddingBottom: 0, paddingRight: 20, width: 0, height: 40)
    }

}

I have created a function to anchor my elements in Extensions.swift:

import UIKit

extension UIView {

    func anchor(top: NSLayoutYAxisAnchor?, left: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, right: NSLayoutXAxisAnchor?, paddingTop: CGFloat, paddingLeft: CGFloat, paddingBottom: CGFloat, paddingRight: CGFloat, width: CGFloat, height: CGFloat) {

        translatesAutoresizingMaskIntoConstraints = false

        if let top = top {
            self.topAnchor.constraint(equalTo: top, constant: paddingTop).isActive = true
        }

        if let left = left {
            self.leftAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true
        }

        if let bottom = bottom {
            self.bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom).isActive = true
        }

        if let right = right {
            self.rightAnchor.constraint(equalTo: right, constant: -paddingRight).isActive = true
        }

        if width != 0 {
            widthAnchor.constraint(equalToConstant: width).isActive = true
        }

        if height != 0 {
            heightAnchor.constraint(equalToConstant: height).isActive = true
        }


    }
}

In addition, I deleted the Application Scene Manifest in Info.plist.

I want my project to support the upcoming iOS versions starting at 13.0. Nevertheless I don't want to use the new framework SwiftUI.

Are there any specific things to watch out for (e.g. Scene Delegate)?

Thank you in advance!


Solution

  • You need to change this

    var emailTextField: UITextField {
    
        let tf = UITextField()
        tf.placeholder = "email";
        tf.backgroundColor = UIColor(white: 0, alpha: 0.03)
        tf.borderStyle = .roundedRect
        tf.font = UIFont.systemFont(ofSize: 14)
    
        return tf
    }
    

    into this

    var emailTextField: UITextField = {
    
        let tf = UITextField()
        tf.placeholder = "email";
        tf.backgroundColor = UIColor(white: 0, alpha: 0.03)
        tf.borderStyle = .roundedRect
        tf.font = UIFont.systemFont(ofSize: 14)
    
        return tf
    }()
    

    In current version emailTextField is computed property, so, every time you access it compiler create a new instance (it works like function, not variable).