rand0m
rand0m

Reputation: 5

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!

Upvotes: 0

Views: 3711

Answers (2)

Sumona Salma
Sumona Salma

Reputation: 137

Textfield was unable to get its parent view while you were adding its constraint. Below code works for me. I passed current view as a param to anchor() method and add emailTextField as a subview there.

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
      emailTextField.anchor(top: view.safeAreaLayoutGuide.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 40, paddingLeft: 20, paddingBottom: 0, paddingRight: 20, width: 0, height: 40, view:view)
   }


 }


extension UIView {

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

        translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(self)
        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
         }
      }
}

Upvotes: 0

AntiVIRUZ
AntiVIRUZ

Reputation: 342

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).

Upvotes: 4

Related Questions