1

Referring this question Making a custom UIView subview that fills its superview while trying to add constraints programmatically.

I am using the code below to add two constraints programmticly; however, I get an error states that "Unable to parse constraint format" because the related view doesn't have a superview.

private func setupDownView(view: UIView) {
    let downView = try? DownView(frame: view.bounds, markdownString: "")
    downView!.translatesAutoresizingMaskIntoConstraints = false
    let viewsDict = ["view": downView!]
    downView!.addConstraint(NSLayoutConstraint.constraints(
        withVisualFormat: "V:|-0-[view]-0-|",
        options: [],
        metrics: nil,
        views: viewsDict)[0])
    downView!.addConstraint(NSLayoutConstraint.constraints(
        withVisualFormat: "H:|-0-[view]-0-|",
        options: [],
        metrics: nil,
        views: viewsDict)[0])
    view.addSubview(downView!);
}

enter image description here

Does the view auto become the superview once it has been added to its subview?

I have tried to add the view before setting up the constrains by addSubview before addConstraint and I get the error "The view hierarchy is not prepared for the constraint".

enter image description here

setupDownView is called in the following two places,

@IBOutlet weak var cardTags: UIView! {didSet { setupDownView(view: cardTags)}}
@IBOutlet weak var cardContent: UIView! {
    didSet {
        setupDownView(view: cardContent)
        let tap = UILongPressGestureRecognizer(
            target: self,
            action: #selector(QuizViewController.tapDownHandler)
        )
        tap.minimumPressDuration = 0
        cardContent.addGestureRecognizer(tap)
    }
}
Community
  • 1
  • 1
XY L
  • 25,431
  • 14
  • 84
  • 143

2 Answers2

4

Constraints need to be added after view is in the hierarchy. So addSubview call must be before addConstraint calls.

Also ensure that addConstraint is called on it's superview, not downView, and that all constraints returned from constraintsWithVisualFormat: are added, not only the first one.

private func setupDownView(view: UIView) {
    let downView = try? DownView(frame: view.bounds, markdownString: "")
    downView!.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(downView!);

    let viewsDict = ["view": downView!]
    view.addConstraints(NSLayoutConstraint.constraints(
        withVisualFormat: "V:|-0-[view]-0-|",
        options: [],
        metrics: nil,
        views: viewsDict))
    view.addConstraints(NSLayoutConstraint.constraints(
        withVisualFormat: "H:|-0-[view]-0-|",
        options: [],
        metrics: nil,
        views: viewsDict))

    view.layoutIfNeeded()
}
Legoless
  • 10,942
  • 7
  • 48
  • 68
1

As of iOS 8, you don't have to figure would which view to add constraints to, just activate them. For individual constraints, just set .isActive to true. To activate an entire array of constraints, use the NSLayoutConstraint.activate() convenience method.

For your code, instead of doing:

view.addConstraints(NSLayoutConstraint.constraints(
    withVisualFormat: "V:|-0-[view]-0-|",
    options: [],
    metrics: nil,
    views: viewsDict))

You'd use:

NSLayoutConstraint.activate(NSLayoutConstraint.constraints(
    withVisualFormat: "V:|-0-[view]-0-|",
    options: [],
    metrics: nil,
    views: viewsDict))

This works because the constraints already know which view or views they refer to, and Cocoa or Cocoa Touch can figure out the appropriate view in the view hierarchy to add the constraints to, which can be tricky depending on how the views are related (eg. siblings, parent/child, other).

The same caveats apply here. You need to make sure the views are in the view hierarchy before activating the constraints, or you will get errors. So, do .addSubview before activating the constraints.

vacawama
  • 150,663
  • 30
  • 266
  • 294