ios - olvide - Agregar mediante programación las restricciones CenterX/CenterY
olvide mi codigo de restricciones iphone 5 (7)
Tengo un UITableViewController que no muestra ninguna sección si no hay nada que mostrar. Agregué una etiqueta para indicarle al usuario que no hay nada que mostrar con este código:
label = UILabel(frame: CGRectMake(20, 20, 250, 100))
label.text = "Nothing to show"
self.tableView.addSubview(label)
Pero ahora, quiero que esté centrado horizontal y verticalmente. Normalmente, elegiría las dos opciones resaltadas (más las de altura y anchura) en la captura de pantalla:
He intentado el siguiente código para agregar las restricciones, pero la aplicación se bloquea con un 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)
error:
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
La etiqueta siempre debe centrarse horizontal y verticalmente porque la aplicación admite la rotación del dispositivo.
¿Qué estoy haciendo mal? ¿Cómo agrego estas restricciones con éxito?
¡Gracias!
Centro en contenedor
El siguiente código hace lo mismo que centrarse en 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
// ...
}
Método 1: estilo de anclaje
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
Método 2: NSLayoutConstraint Style
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
Notas
-
El estilo de anclaje es el método preferido sobre
NSLayoutConstraint
Style, sin embargo, solo está disponible desde iOS 9, por lo que si es compatible con iOS 8, aún debe usarNSLayoutConstraint
Style. - También necesitará agregar restricciones de longitud y ancho.
- Mi respuesta completa está here .
El equivalente de ObjectiveC es:
myView.translatesAutoresizingMaskIntoConstraints = NO;
[[myView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor] setActive:YES];
[[myView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor] setActive:YES];
En Swift 5 se ve así:
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)
Programáticamente puede hacerlo agregando las siguientes restricciones.
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];
Si no le importa que esta pregunta sea específicamente sobre una vista de tabla, y simplemente desea centrar una vista encima de otra vista, aquí está:
label.translatesAutoresizingMaskIntoConstraints = false
label.centerXAnchor.constraint(equalTo: vc.view.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: vc.view.centerYAnchor).isActive = true
Una solución para mí fue crear un
UILabel
y agregarlo al
UIButton
como una subvista.
Finalmente agregué una restricción para centrarlo dentro del botón.
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;
Actualización para Swift 3 / Swift 4:
A partir de iOS 8, puede y debe activar sus restricciones estableciendo su propiedad
isActive
en
true
.
Esto permite que las restricciones se agreguen a las vistas adecuadas.
Puede activar varias restricciones a la vez pasando una matriz que contiene las restricciones a
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])
Mejor solución
Dado que esta pregunta se respondió originalmente, se introdujeron los anclajes de diseño, lo que facilita mucho la creación de restricciones. En este ejemplo, creo las restricciones y las activo de inmediato:
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
o lo mismo usando
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)
])
Nota: Agregue siempre sus subvistas a la jerarquía de vistas antes de crear y activar las restricciones.
Respuesta original
Las restricciones hacen referencia a
self.tableView
.
Como está agregando la etiqueta como una subvista de
self.tableView
, las restricciones deben agregarse al "ancestro común":
self.tableView.addConstraint(xConstraint)
self.tableView.addConstraint(yConstraint)
Como @mustafa y @kcstricks señalaron en los comentarios, debe establecer
label.translatesAutoresizingMaskIntoConstraints
en
false
.
Cuando hace esto, también necesita especificar el
width
y el
height
de la etiqueta con restricciones porque el marco ya no se usa.
Finalmente, también debe establecer
textAlignment
en
.Center
para que su texto esté centrado en su etiqueta.
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)