以编程方式添加 CenterX/CenterY 约束

以编程方式添加 CenterX/CenterY 约束

问题描述:

我有一个 UITableViewController,如果没有可显示的内容,它不会显示任何部分.我添加了一个标签来向用户表明此代码没有可显示的内容:

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:

我已尝试使用以下代码添加约束,但应用程序因错误而崩溃:

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)

错误:

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?

谢谢!

Swift 3/Swift 4 的更新:

从 iOS 8 开始,您可以并且应该通过将约束的 isActive 属性设置为 true 来激活约束.这使约束能够将自己添加到正确的视图中.您可以通过将包含约束的数组传递给 NSLayoutConstraint.activate()

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

更好的解决方案:

由于最初回答了这个问题,因此引入了布局锚点,使创建约束变得更加容易.在这个例子中,我创建了约束并立即激活它们:

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

或同样使用 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.

原答案:

约束引用self.tableView.由于您将标签添加为 self.tableView 的子视图,因此需要将约束添加到共同祖先":

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)

正如@mustafa 和@kcstricks 在评论中指出的,您需要将label.translatesAutoresizingMaskIntoConstraints 设置为false.执行此操作时,还需要指定带有约束的标签的 widthheight,因为不再使用框架.最后,您还应该将 textAlignment 设置为 .Center,以便您的文本在标签中居中.


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)