Aggiungere vincoli CenterX / CenterY a livello di codice


84

Ho un UITableViewController che non visualizza alcuna sezione se non c'è nulla da mostrare. Ho aggiunto un'etichetta per indicare all'utente che non c'è nulla da visualizzare con questo codice:

label = UILabel(frame: CGRectMake(20, 20, 250, 100))
label.text = "Nothing to show"
self.tableView.addSubview(label)

Ma ora, voglio che sia centrato orizzontalmente e verticalmente. Normalmente, sceglierei le due opzioni evidenziate (più quelle per altezza e larghezza) nello screenshot:

inserisci qui la descrizione dell'immagine

Ho provato il seguente codice per aggiungere i vincoli ma l'app si arresta in modo anomalo con errore:

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)

errore:

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

L'etichetta deve essere sempre centrata orizzontalmente e verticalmente perché l'app supporta la rotazione del dispositivo.

Che cosa sto facendo di sbagliato? Come aggiungo correttamente questi vincoli?

Grazie!


2
STO CREANDO QUESTO IN VIEWDIDLOAD.
Mario A Guzman

2
Non vedo la riga in cui aggiungi l'etichetta come sottoview a un'altra vista. È importante che tu lo faccia prima di aggiungere dei vincoli.
Scott Berrevoets

Io sono: label = UILabel (frame: CGRectMake (20, 20, 250, 100)) label.text = "Nothing to show" self.tableView.addSubview (label)
Mario A Guzman

Risposte:


164

Aggiornamento per Swift 3 / Swift 4:

A partire da iOS 8, puoi e devi attivare i tuoi vincoli impostando la loro isActiveproprietà su true. Ciò consente ai vincoli di aggiungersi alle visualizzazioni corrette. È possibile attivare più vincoli contemporaneamente passando un array contenente i vincoli aNSLayoutConstraint.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])

Soluzione migliore:

Poiché questa domanda è stata originariamente risolta, sono stati introdotti gli ancoraggi del layout rendendo molto più facile creare i vincoli. In questo esempio creo i vincoli e li attivo immediatamente:

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 stesso utilizzando 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: aggiungi sempre le tue viste secondarie alla gerarchia della vista prima di creare e attivare i vincoli.


Risposta originale:

I vincoli fanno riferimento a self.tableView. Dal momento che stai aggiungendo l'etichetta come una vista secondaria di self.tableView, i vincoli devono essere aggiunti a "antenato comune":

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

Come @mustafa e @kcstricks sottolineato nei commenti, è necessario impostare label.translatesAutoresizingMaskIntoConstraintsa false. Quando si esegue questa operazione, è inoltre necessario specificare il widthe heightdell'etichetta con vincoli perché la cornice non viene più utilizzata. Infine, dovresti anche impostare textAlignmentsu in .Centermodo che il tuo testo sia centrato nell'etichetta.

    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)

Eccellente. Grazie.
App Dev Guy

1
le ancore hanno funzionato per me- Apprezzo il tuo impegno!
FullMetalFist

37

Centro nel contenitore

inserisci qui la descrizione dell'immagine

Il codice seguente fa la stessa cosa del centraggio in 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
    // ...
}

Metodo 1: stile di ancoraggio

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

Metodo 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

Appunti

  • Lo stile di ancoraggio è il metodo preferito rispetto allo NSLayoutConstraintstile, tuttavia è disponibile solo da iOS 9, quindi se stai supportando iOS 8, dovresti comunque usare lo NSLayoutConstraintstile.
  • Sarà inoltre necessario aggiungere vincoli di lunghezza e larghezza.
  • La mia risposta completa è qui .

10

L'equivalente ObjectiveC è:

    myView.translatesAutoresizingMaskIntoConstraints = NO;

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

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

migliore per me, grazie
Fadi Abuzant

8

A livello di codice puoi farlo aggiungendo i seguenti vincoli.

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 prega di scambiare vincolo
Verticale

6

In Swift 5 assomiglia a questo:

label.translatesAutoresizingMaskIntoConstraints = false
label.centerXAnchor.constraint(equalTo: vc.view.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: vc.view.centerYAnchor).isActive = true

1

Se non ti interessa che questa domanda riguardi specificamente una vista da tavolo e desideri solo centrare una vista sopra un'altra vista, ecco per farlo:

    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

Una soluzione per me è stata creare un UILabele aggiungerlo a UIButtoncome sottoview. Infine ho aggiunto un vincolo per centrarlo all'interno del pulsante.

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; 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.