У меня есть 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
Ярлык всегда должен располагаться по центру по горизонтали и вертикали, поскольку приложение поддерживает вращение устройства.
Что я делаю неправильно? Как мне успешно добавить эти ограничения?
Благодаря!
ios
xcode
swift
autolayout
nslayoutconstraint
Марио Гусман
источник
источник
Ответы:
Обновление для 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)
источник
Центр в контейнере
Приведенный ниже код делает то же самое, что и центрирование в Интерфейсном Разработчике.
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
стиль.источник
Эквивалент ObjectiveC:
myView.translatesAutoresizingMaskIntoConstraints = NO; [[myView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor] setActive:YES]; [[myView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor] setActive:YES];
источник
Программно вы можете сделать это, добавив следующие ограничения.
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];
источник
В Swift 5 это выглядит так:
label.translatesAutoresizingMaskIntoConstraints = false label.centerXAnchor.constraint(equalTo: vc.view.centerXAnchor).isActive = true label.centerYAnchor.constraint(equalTo: vc.view.centerYAnchor).isActive = true
источник
Если вас не волнует, что этот вопрос касается конкретно табличного представления, и вы просто хотите центрировать одно представление поверх другого представления, вот для этого:
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)
источник
Решением для меня было создать
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;
источник