I am a newbie swift developer, following code examples in a good iOS 9 Swift book. The book has just demonstrated how to add a constraint (in code) between two objects (label, button) that are in two different branches of the view hierarchy.
myLabel is in viewBB container, which is in the top view controller. myButton is in the top view controller, outside of viewBB.
The example code worked fine. But for an exercise, I wanted to create a second label2 inside of viewBB, and constrain its position to be offset from myLabel. So both labels (I think) are in viewBB.
Creating label2 in code and adding the constraints in code works fine.
But... if I create label2 in the interface builder, and try to remove the IB constraints and replace them with my own, the build succeeds but the app crashes with the usual "view hierarchy not prepared" error.
*** The interesting thing is the error message talks about a label and a button, suggesting that the book example code (I called it BLOCK 0 below) is the offender.
What am I doing wrong? (I've read everything I can find on the error message, including 4 posts in this forum. But I'm at a loss to know why the book code is failing.)
The error message:
2016-02-25 12:52:32.796 TwoViewConstraints[5713:1860768] The view hierarchy is not prepared for the constraint: NSLayoutConstraint:0x7c9ce990 UILabel:0x7c9dbec0'Label'.centerX == UIButton:0x7c9deed0'Button'.centerX
When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] to debug.
Message from debugger: Terminated due to signal 15
Here is my code:
class ViewController: UIViewController {
@IBOutlet weak var myLabel: UILabel!
@IBOutlet weak var myButton: UIButton!
@IBOutlet weak var centerConstraint: NSLayoutConstraint!
@IBOutlet weak var viewBB: UIView!
@IBOutlet weak var lab2cxh: NSLayoutConstraint!
@IBOutlet weak var lab2cxv: NSLayoutConstraint!
@IBOutlet weak var label2: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// BLOCK 0 - works fine, from the iOS9 book
// myLabel and myButton were created with the interface builder
// Goal is to remove an IB constraint, and replace it with one
// that is built in code. (Because label and buttons are in
// different branches of the view hierarchy).
//
// remove auto center constraint from parent label
viewBB.removeConstraint(centerConstraint)
// center the label and the button across views
let ccx = NSLayoutConstraint(item: myLabel,
attribute: NSLayoutAttribute.CenterX,
relatedBy: NSLayoutRelation.Equal,
toItem: myButton,
attribute: NSLayoutAttribute.CenterX,
multiplier: 1.0,
constant: 0)
self.view.addConstraint(ccx)
// BLOCK 1 - Create a second label object in code
// this code works fine with the two added constraints below
// create a second label object and add it to the view
// let label2 = UILabel()
// label2.translatesAutoresizingMaskIntoConstraints = false
// label2.text="Label2"
// viewBB.addSubview(label2)
// BLOCK 2 - create a second label object in the interface builder
// Suggested horiz/vert constraints are set in the IB.
// So now I want to remove them, and replace them with my own,
// as was done in the Swift book example code in BLOCK 0.
// remove original label 2 constraints
viewBB.removeConstraint(lab2cxv)
viewBB.removeConstraint(lab2cxh)
// adding these two constraints works fine when the label2 object
// is created in code. But when I create label2 in the interface
// builder and try to remove the IB constraints (as was done in the
// first block of code), I get the error:
//
// ... "The view hierarchy is not prepared
// for the constraint "Label.CenterX to Button.CenterX", which is
// the first block of code.
//
// NOTE** To me, it looks like the error is coming from the
// BLOCK 0 constraint, since it cites a label and a button, not
// two labels.
// What am I doing wrong?
let c2h = NSLayoutConstraint(item: label2,
attribute: NSLayoutAttribute.CenterY,
relatedBy: NSLayoutRelation.Equal,
toItem: myLabel,
attribute: NSLayoutAttribute.CenterY,
multiplier: 1.0,
constant: -50)
self.viewBB.addConstraint(c2h)
// constrain label 2 to be diagonally left of label one
let c2v = NSLayoutConstraint(item: label2,
attribute: NSLayoutAttribute.CenterX,
relatedBy: NSLayoutRelation.Equal,
toItem: myLabel,
attribute: NSLayoutAttribute.CenterX,
multiplier: 1.0,
constant: -100)
self.viewBB.addConstraint(c2v)
}
ccx.active = trueinstead ofself.view.addConstraint(ccx). And use better names for your properties. - dasdom