Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically Add CenterX/CenterY Constraints

I have a UITableViewController that doesn't display any sections if there is nothing to show. I've added a label to indicate to the user that there is nothing to display with this code:

label = UILabel(frame: CGRectMake(20, 20, 250, 100)) label.text = "Nothing to show" self.tableView.addSubview(label) 

But now, I want it to be centered Horizontally and Vertically. Normally, I would choose the two options highlighted (plus ones for height and width) in the screenshot:

enter image description here

I've tried the following code to add the constraints but the app crashes with error:

label = UILabel(frame: CGRectMake(20, 20, 250, 100)) label.text = "Nothing to show"  let xConstraint = NSLayoutConstraint(item: label, attribute: .CenterX, relatedBy: .Equal, toItem: self.tableView, attribute: .CenterX, multiplier: 1, constant: 0) let yConstraint = NSLayoutConstraint(item: label, attribute: .CenterY, relatedBy: .Equal, toItem: self.tableView, attribute: .CenterY, multiplier: 1, constant: 0)  label.addConstraint(xConstraint) label.addConstraint(yConstraint) 

error:

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 _viewHierarchyUnpreparedForConstraint:] to debug. 2014-12-23 08:17:36.755 [982:227877] *** Assertion failure in -[UILabel _layoutEngine_didAddLayoutConstraint:roundingAdjustment:mutuallyExclusiveConstraints:], /SourceCache/UIKit/UIKit-3318.16.21/NSLayoutConstraint_UIKitAdditions.m:560 

The label should always center horizontally and vertically because the app supports rotation of the device.

What am I doing wrong? How do I add these constraints successfully?

Thanks!

like image 591
Mario A Guzman Avatar asked Dec 23 '14 16:12

Mario A Guzman


People also ask

How do you add constraints in Xcode?

To create constraints select the button and click the Align icon in the auto layout menu. A popover menu will appear, check both “Horizontal in container” and “Vertically in container” options to center the button on the screen. Then click the “Add 2 Constraints” button. Run the application.

What is Nslayoutconstraint?

The relationship between two user interface objects that must be satisfied by the constraint-based layout system.


1 Answers

Update for Swift 3/Swift 4:

As of iOS 8, you can and should activate your constraints by setting their isActive property to true. This enables the constraints to add themselves to the proper views. You can activate multiple constraints at once by passing an array containing the constraints to NSLayoutConstraint.activate()

let label = UILabel(frame: CGRect.zero) label.text = "Nothing to show" label.textAlignment = .center label.backgroundColor = .red  // Set background color to see if label is centered label.translatesAutoresizingMaskIntoConstraints = false self.tableView.addSubview(label)  let widthConstraint = NSLayoutConstraint(item: label, attribute: .width, relatedBy: .equal,                                          toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 250)  let heightConstraint = NSLayoutConstraint(item: label, attribute: .height, relatedBy: .equal,                                           toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 100)  let xConstraint = NSLayoutConstraint(item: label, attribute: .centerX, relatedBy: .equal, toItem: self.tableView, attribute: .centerX, multiplier: 1, constant: 0)  let yConstraint = NSLayoutConstraint(item: label, attribute: .centerY, relatedBy: .equal, toItem: self.tableView, attribute: .centerY, multiplier: 1, constant: 0)  NSLayoutConstraint.activate([widthConstraint, heightConstraint, xConstraint, yConstraint]) 

Better Solution:

Since this question was originally answered, layout anchors were introduced making it much easier to create the constraints. In this example I create the constraints and immediately activate them:

label.widthAnchor.constraint(equalToConstant: 250).isActive = true label.heightAnchor.constraint(equalToConstant: 100).isActive = true label.centerXAnchor.constraint(equalTo: self.tableView.centerXAnchor).isActive = true label.centerYAnchor.constraint(equalTo: self.tableView.centerYAnchor).isActive = true 

or the same using NSLayoutConstraint.activate():

NSLayoutConstraint.activate([     label.widthAnchor.constraint(equalToConstant: 250),     label.heightAnchor.constraint(equalToConstant: 100),     label.centerXAnchor.constraint(equalTo: self.tableView.centerXAnchor),     label.centerYAnchor.constraint(equalTo: self.tableView.centerYAnchor) ]) 

Note: Always add your subviews to the view hierarchy before creating and activating the constraints.


Original Answer:

The constraints make reference to self.tableView. Since you are adding the label as a subview of self.tableView, the constraints need to be added to the "common ancestor":

   self.tableView.addConstraint(xConstraint)    self.tableView.addConstraint(yConstraint) 

As @mustafa and @kcstricks pointed out in the comments, you need to set label.translatesAutoresizingMaskIntoConstraints to false. When you do this, you also need to specify the width and height of the label with constraints because the frame no longer is used. Finally, you also should set the textAlignment to .Center so that your text is centered in your label.

    var  label = UILabel(frame: CGRectZero)     label.text = "Nothing to show"     label.textAlignment = .Center     label.backgroundColor = UIColor.redColor()  // Set background color to see if label is centered     label.translatesAutoresizingMaskIntoConstraints = false     self.tableView.addSubview(label)      let widthConstraint = NSLayoutConstraint(item: label, attribute: .Width, relatedBy: .Equal,         toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 250)     label.addConstraint(widthConstraint)      let heightConstraint = NSLayoutConstraint(item: label, attribute: .Height, relatedBy: .Equal,         toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 100)     label.addConstraint(heightConstraint)      let xConstraint = NSLayoutConstraint(item: label, attribute: .CenterX, relatedBy: .Equal, toItem: self.tableView, attribute: .CenterX, multiplier: 1, constant: 0)      let yConstraint = NSLayoutConstraint(item: label, attribute: .CenterY, relatedBy: .Equal, toItem: self.tableView, attribute: .CenterY, multiplier: 1, constant: 0)      self.tableView.addConstraint(xConstraint)     self.tableView.addConstraint(yConstraint) 
like image 75
vacawama Avatar answered Sep 23 '22 02:09

vacawama