Agregar programáticamente CenterX / CenterY Constraints

Tengo un UITableViewController que no muestra ninguna sección si no hay nada que mostrar. Agregué una label para indicar 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 se centre horizontal y verticalmente. Normalmente, elegiría las dos opciones resaltadas (más las de alto y ancho) en la captura de pantalla:

introduzca la descripción de la imagen aquí

He intentado el siguiente código para agregar las restricciones, pero la aplicación falla 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 _viewHierarchyUnprepanetworkingForConstraint:] 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 label siempre debe centrarse horizontal y verticalmente porque la aplicación admite la rotation del dispositivo.

¿Qué estoy haciendo mal? ¿Cómo agrego estas restricciones con éxito?

¡Gracias!

Actualización para iOS 8 y posterior:

[Gracias a @ChrisHatton por recordarme que actualice la respuesta]

A partir de iOS 8, puede y debe activar sus restricciones estableciendo su propiedad active 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.activateConstraints()

  let label = UILabel(frame: CGRectZero) label.text = "Nothing to show" label.textAlignment = .Center label.backgroundColor = .networkingColor() // Set background color to see if label is centenetworking 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.activateConstraints([widthConstraint, heightConstraint, xConstraint, yConstraint]) 

Respuesta original:

Las restricciones hacen reference a self.tableView . Dado que está agregando la label como una subvista de self.tableView , las restricciones deben agregarse al "antepasado 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 debe especificar el width y height de la label con restricciones porque el marco ya no se usa. Finalmente, también debe configurar textAlignment en .Center para que el text se centre en su label.

  var label = UILabel(frame: CGRectZero) label.text = "Nothing to show" label.textAlignment = .Center label.backgroundColor = UIColor.networkingColor() // Set background color to see if label is centenetworking 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) 

Actualizado para Swift 3

Centro en contenedor

introduzca la descripción de la imagen aquí

El código a continuación hace lo mismo que centrar en el generador de interfaces.

 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: Estilo NSLayoutConstraint

 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 

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 está soportando iOS 8, debería seguir utilizando NSLayoutConstraint Style.
  • También deberá agregar restricciones de longitud y ancho.
  • Mi respuesta completa está aquí .

Programáticamente puedes 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]; 

El equivalente de ObjectiveC es:

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

Si no le importa que esta pregunta sea específicamente sobre una vista de tabla, y simplemente desea centrar una vista en la parte superior de otra vista, aquí debe hacerlo:

  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)