42
votes

I add a UILabel (amountLabel) in UIViewController in storyboard editor. And then in swift file, viewDidLoad, I programatically create a UITextField (paymentTextField) and try to add a constraint between amountLabel and paymentTextField. Here is my code in viewDidload:

let paymentTextField = UITextField()
    paymentTextField.translatesAutoresizingMaskIntoConstraints = false
    paymentTextField.frame = CGRectMake(15, 100, CGRectGetWidth(self.view.frame) - 30, 44)
    self.view.addSubview(paymentTextField)

    let bottonConstraint = NSLayoutConstraint(item: paymentTextField, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self.amountLabel , attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 30)
    bottonConstraint.identifier = "paymentTextFieldSpacing"
    NSLayoutConstraint.activateConstraints([bottonConstraint])

But I get an error:

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

Does anyone know what is wrong? amountLabel is directly dragged to the view in storyboard and "paymentTextField" is added programmatically to the same view. Why have they no common ancestor?

6

6 Answers

150
votes

I ran into the same problem that you described earlier. In order to make the programmatic subview, (in your case the paymentTextField) you have to add this to the subview first and then apply your constraints.

By adding the subview to view first, this ensures both views have the same parent.

23
votes

Checklist for this ISSUE:

  • Check whether you added the programmatically created view to its parent before activating constraints
  • Check whether you write constraints activation code inside viewDidLoad() / viewWillAppear(). You should write constraints activation code in updateViewConstraints or viewWillLayoutSubviews. ( suggested by vmeyer )
  • Check whether you turn off translatesAutoresizingMaskIntoConstraints.
12
votes

The error states that "because they have no common ancestor", which means that they don't share the same parent. In order to relate constraint between two items, they have to have a child-parent relationship or a sibling one.

In your case just make sure they have the same parent view before adding the constraint programmatically.

6
votes

Make sure you added the paymentTextField to your view:

paymentTextField.translatesAutoResizingMaskIntoConstraints = false
view.addSubview(paymentTextField)

...

Add your constraints, for example paymentTextField.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true

1
votes
let nameLabel: UILabel = {

   let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()

if you forgot setting this label.translatesAutoresizingMaskIntoConstraints = false or if you add subview after constraints this problem arises

0
votes

SWIFT 4 & 5

final class VoiceSearchViewController: UIViewController {
   // MARK: - Properties
   var backgroundView: UIView = {
     let view = UIView()
     view.layer.cornerRadius = 20
     view.backgroundColor = .red
     return view
   }()

  // MARK: - Life Cycle Methods
  override func viewDidLoad() {
    super.viewDidLoad()
    initialSetup()
  }

  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    dismiss(animated: true, completion: nil)
  }

  // MARK: - Private Methods
  private func initialSetup() {
    view.backgroundColor = .clear
    view.addSubview(backgroundView)
    backgroundView.translatesAutoresizingMaskIntoConstraints = false
    backgroundView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    backgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
    backgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
    backgroundView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5).isActive = true
  }
}