【问题标题】:Programmatically Add CenterX/CenterY Constraints以编程方式添加 CenterX/CenterY 约束
【发布时间】:2015-02-21 19:38:06
【问题描述】:

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

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

但是现在,我希望它水平和垂直居中。通常,我会选择屏幕截图中突出显示的两个选项(加上高度和宽度):

我尝试了以下代码来添加约束,但应用程序崩溃并出现错误:

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

标签应始终水平和垂直居中,因为应用支持设备旋转。

我做错了什么?如何成功添加这些约束?

谢谢!

【问题讨论】:

  • 我在 VIEWDIDLOAD 中创建这个。
  • 我没有看到您将标签作为子视图添加到其他视图的行。在添加约束之前这样做很重要。
  • 我是:label = UILabel(frame: CGRectMake(20, 20, 250, 100)) label.text = "Nothing to show" self.tableView.addSubview(label)

标签: ios xcode swift autolayout nslayoutconstraint


【解决方案1】:

我的一个解决方案是创建一个UILabel 并将其作为子视图添加到UIButton。最后我添加了一个约束使其在按钮内居中。

UILabel * myTextLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 75, 75)];
myTextLabel.text = @"Some Text";
myTextLabel.translatesAutoresizingMaskIntoConstraints = false;

[myButton addSubView:myTextLabel];

// Add Constraints
[[myTextLabel centerYAnchor] constraintEqualToAnchor:myButton.centerYAnchor].active = true;
[[myTextLabel centerXAnchor] constraintEqualToAnchor:myButton.centerXAnchor].active = true; 

【讨论】:

    【解决方案2】:

    在 Swift 5 中它看起来像这样:

    label.translatesAutoresizingMaskIntoConstraints = false
    label.centerXAnchor.constraint(equalTo: vc.view.centerXAnchor).isActive = true
    label.centerYAnchor.constraint(equalTo: vc.view.centerYAnchor).isActive = true
    

    【讨论】:

      【解决方案3】:

      Swift 3/Swift 4 更新:

      从 iOS 8 开始,您可以并且应该通过将约束的 isActive 属性设置为 true 来激活约束。这使约束能够将自己添加到正确的视图中。您可以通过将包含约束的数组传递给 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])
      

      更好的解决方案:

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

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

      注意:始终在创建和激活约束之前将您的子视图添加到视图层次结构中。


      原答案:

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

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

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

          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)
      

      【讨论】:

      • 优秀。谢谢。
      • 锚对我有用——感谢您的努力!
      【解决方案4】:

      容器中心

      下面的代码与 Interface Builder 中的居中功能相同。

      override func viewDidLoad() {
          super.viewDidLoad()
      
          // set up the view
          let myView = UIView()
          myView.backgroundColor = UIColor.blue
          myView.translatesAutoresizingMaskIntoConstraints = false
          view.addSubview(myView)
      
          // Add code for one of the constraint methods below
          // ...
      }
      

      方法一:锚样式

      myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
      myView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
      

      方法二:NSLayoutConstraint 样式

      NSLayoutConstraint(item: myView, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1, constant: 0).isActive = true
      NSLayoutConstraint(item: myView, attribute: NSLayoutConstraint.Attribute.centerY, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerY, multiplier: 1, constant: 0).isActive = true
      

      注意事项

      • 锚样式是优于NSLayoutConstraint 样式的首选方法,但它仅适用于iOS 9,因此如果您支持iOS 8,则仍应使用NSLayoutConstraint 样式。
      • 您还需要添加长度和宽度限制。
      • 我的完整答案是here

      【讨论】:

        【解决方案5】:

        ObjectiveC 的等价物是:

            myView.translatesAutoresizingMaskIntoConstraints = NO;
        
            [[myView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor] setActive:YES];
        
            [[myView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor] setActive:YES];
        

        【讨论】:

        • 对我来说最好的,谢谢
        【解决方案6】:

        您可以通过添加以下约束以编程方式做到这一点。

        NSLayoutConstraint *constraintHorizontal = [NSLayoutConstraint constraintWithItem:self  
                                                                              attribute:NSLayoutAttributeCenterX 
                                                                              relatedBy:NSLayoutRelationEqual 
                                                                                 toItem:self.superview 
                                                                              attribute:attribute 
                                                                             multiplier:1.0f 
                                                                               constant:0.0f];
        
        NSLayoutConstraint *constraintVertical = [NSLayoutConstraint constraintWithItem:self
                                                                                attribute:NSLayoutAttributeCenterY 
                                                                                relatedBy:NSLayoutRelationEqual
                                                                                   toItem:self.superview 
                                                                                attribute:attribute 
                                                                               multiplier:1.0f
                                                                                 constant:0.0f];
        

        【讨论】:

        • 请将 constraintVertical 与 constraintHorizo​​ntal 交换 - 这很混乱
        【解决方案7】:

        如果您不关心这个问题是专门针对 tableview 的,并且您只想将一个视图居中放置在另一个视图之上,那么可以这样做:

            let horizontalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: parentView, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0)
            parentView.addConstraint(horizontalConstraint)
        
            let verticalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: parentView, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0)
            parentView.addConstraint(verticalConstraint)
        

        【讨论】:

          猜你喜欢
          • 2021-10-29
          • 1970-01-01
          • 2016-05-28
          • 1970-01-01
          • 2015-09-24
          • 1970-01-01
          相关资源
          最近更新 更多