Программно Добавьте Ограничения CenterX/CenterY


У меня есть 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

Метка всегда должна располагаться по центру горизонтально и вертикально, поскольку приложение поддерживает вращение устройства.

Что я делаю не так? Как мне успешно добавить эти ограничения?

Спасибо!

5 42

5 ответов:

Обновление для 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

Примечание: всегда добавляйте свои подвиды в иерархию представлений перед созданием и активацией ограничений.


Оригинальный Ответ:

Ограничения ссылаются на self.tableView. Поскольку вы добавляете метку в качестве подвида self.tableView, ограничения необходимо добавить к "общему предку":

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

Как @mustafa и @kcstricks указал в комментариях, что вам нужно установить label.translatesAutoresizingMaskIntoConstraints в false. Когда вы делаете это, вам также нужно указать width и height метки с ограничениями, потому что фрейм больше не используется. Наконец, вы также должны установить 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)

обновлено для Swift 3

Центр в контейнере

Введите описание изображения здесь

Приведенный ниже код делает то же самое, что и центрирование в Построителе интерфейсов.
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
    // ...
}

Метод 1: Якорный Стиль

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

Метод 2: Nslayoutconstraint Style

NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0).active = true
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0).active = true

Примечания

  • Anchor style является предпочтительным методом по сравнению со стилем NSLayoutConstraint, однако он доступен только в iOS 9, поэтому если вы поддерживаете iOS 8, вы все равно должны использовать NSLayoutConstraint стиль.
  • вам также потребуется добавить ограничения длины и ширины.
  • мой полный ответ - здесь.

Программно это можно сделать, добавив следующие ограничения.

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

Эквивалент ObjectiveC:

    myView.translatesAutoresizingMaskIntoConstraints = NO;

    [[myView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor] setActive:YES];

    [[myView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor] setActive:YES];

Если вы не заботитесь о том, чтобы этот вопрос был конкретно о 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)