Программное добавление ограничений CenterX / CenterY

84

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

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

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

Благодаря!

Марио Гусман
источник
2
Я СОЗДАЮ ЭТО В ВИДЕ ЗАГРУЗКИ.
Марио Гусман
2
Я не вижу строки, в которой вы добавляете метку в качестве подпредставления к другому представлению. Важно сделать это перед добавлением ограничений.
Скотт Берревоутс
Я: label = UILabel (frame: CGRectMake (20, 20, 250, 100)) label.text = "Нечего показывать" self.tableView.addSubview (label)
Марио А. Гусман

Ответы:

164

Обновление для 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, вам необходимо установить 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)
Вачавама
источник
Превосходно. Спасибо.
App Dev Guy
1
якоря работали на меня - я ценю ваши усилия!
FullMetalFist
37

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

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

Приведенный ниже код делает то же самое, что и центрирование в Интерфейсном Разработчике.

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

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 стиль.
  • Вам также потребуется добавить ограничения по длине и ширине.
  • Мой полный ответ здесь .
Suragch
источник
10

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

    myView.translatesAutoresizingMaskIntoConstraints = NO;

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

    [[myView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor] setActive:YES];
Пол Джи
источник
лучший для меня, спасибо
Фади Абузант
8

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

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];
YaBoiSandeep
источник
Пожалуйста, поменяйте местами ограничение
горизонтальное
6

В Swift 5 это выглядит так:

label.translatesAutoresizingMaskIntoConstraints = false
label.centerXAnchor.constraint(equalTo: vc.view.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: vc.view.centerYAnchor).isActive = true
PiKey
источник
1

Если вас не волнует, что этот вопрос касается конкретно табличного представления, и вы просто хотите центрировать одно представление поверх другого представления, вот для этого:

    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)
Адам Калнас
источник
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; 
coletrain
источник