Centrare una vista nella sua superview usando Visual Format Language


160

Ho appena iniziato a studiare AutoLayout per iOS e ho dato un'occhiata a Visual Format Language.

Funziona tutto bene tranne una cosa: non riesco proprio a centrare una vista nella sua superview.
È possibile con VFL o devo creare manualmente un vincolo?

Risposte:


232

Attualmente no, non sembra che sia possibile centrare una vista nella superview usando solo VFL. Tuttavia, non è così difficile farlo utilizzando una singola stringa VFL e un singolo vincolo aggiuntivo (per asse):

VFL: "|-(>=20)-[view]-(>=20)-|"

[NSLayoutConstraint constraintWithItem:view
                             attribute:NSLayoutAttributeCenterX
                             relatedBy:NSLayoutRelationEqual
                                toItem:view.superview
                             attribute:NSLayoutAttributeCenterX
                            multiplier:1.f constant:0.f];

Si potrebbe pensare che si sarebbe semplicemente in grado di fare questo (che è quello che inizialmente ho pensato e provato quando ho visto questa domanda):

[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=20)-[view(==200)]-(>=20)-|"
                                 options: NSLayoutFormatAlignAllCenterX | NSLayoutFormatAlignAllCenterY
                                 metrics:nil
                                   views:@{@"view" : view}];

Ho provato molte diverse varianti di quanto sopra cercando di piegarlo alla mia volontà, ma questo non sembra applicarsi alla superview anche quando hanno esplicitamente due stringhe VFL separate per entrambi gli assi ( H:|V:). Poi ho iniziato a cercare di isolare esattamente quando le opzioni non vengono applicate al VFL. Sembrano non applicarsi alla superview nella VFL e si applicheranno solo a tutte le visualizzazioni esplicite menzionate nella stringa VFL (che in alcuni casi è deludente).

Spero che in futuro Apple aggiunga una sorta di nuova opzione per fare in modo che le opzioni VFL tengano conto della superview, anche se lo fanno solo quando esiste una sola visualizzazione esplicita oltre alla superview nella VFL. Un'altra soluzione potrebbe essere un'altra opzione passata nel VFL che dice qualcosa come: NSLayoutFormatOptionIncludeSuperview.

Inutile dire che ho imparato molto sul VFL cercando di rispondere a questa domanda.


2
Grazie per quella risposta dettagliata. L'unica ragione per cui uso VFL, tuttavia, è sbarazzarsi di quei brutti pezzi di vincoli. Peccato che Apple non lo supporti ancora ...
Christian Schnorr,

@larsacus Potresti commentare perché la risposta qui sotto funziona? Mi ha lasciato perplesso.
Matt G,

8
La risposta di Evgeny qui sotto funziona perché il motore di autolayout tratta |e [superview]come entità interne separate anche se rappresentano lo stesso oggetto semantico. Usando [superview], autolayout include l'oggetto superview nelle sue metriche di allineamento (nel caso nella sua risposta al link github, è la metrica NSLayoutFormatAlignAllCenterY/ X).
Larsacus,

@larsacus Fantastico, grazie!
Matt G,

Sappiamo se la limitazione si applica ancora con la versione di VFL fornita con l'attuale iOS 7.x?
Drux,

138

Sì, è possibile centrare una vista nella sua superview con Visual Format Language. Sia in verticale che in orizzontale. Ecco la demo:

https://github.com/evgenyneu/center-vfl


15
È fantastico, pensi di poterci espandere e spiegare perché funziona?
Tapi,

3
Ho finito per utilizzare qualcosa di più vicino alla risposta contrassegnata, ma +1 per un allegato github nel tuo
Rembrandt Q. Einstein,

4
@Dan Se non funziona per te, potrebbe essere necessario disporre di vincoli per larghezza e altezza. Il VFL sarebbe così per larghezza: @ "[etichetta (== 200)]" e così per altezza: @ "V: [etichetta (== 200)]"
Jonathan Zhan

1
Perché i risultati da | e [superview] diverso? È qualcosa che dovrebbe essere radar o sta succedendo che non sto seguendo?
Matt G,

3
Impossibile analizzare il formato del vincolo: le opzioni mascherano le viste che devono essere allineate su un bordo orizzontale, il che non è consentito per il layout che è anche orizzontale. H: [superview] - (<= 1) - [label1]
grabner

82

Penso che sia meglio creare manualmente dei vincoli.

[superview addConstraint:
 [NSLayoutConstraint constraintWithItem:view
                              attribute:NSLayoutAttributeCenterX
                              relatedBy:NSLayoutRelationEqual
                                 toItem:superview
                              attribute:NSLayoutAttributeCenterX
                             multiplier:1
                               constant:0]];
[superview addConstraint:
 [NSLayoutConstraint constraintWithItem:view
                              attribute:NSLayoutAttributeCenterY
                              relatedBy:NSLayoutRelationEqual
                                 toItem:superview
                              attribute:NSLayoutAttributeCenterY
                             multiplier:1
                               constant:0]];

Ora possiamo usare NSLayoutAnchor per definire programmaticamente AutoLayout :

(Disponibile in iOS 9.0 e versioni successive)

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

Consiglio di utilizzare SnapKit , che è un DSL per semplificare il layout automatico su iOS e macOS.

view.snp.makeConstraints({ $0.center.equalToSuperview() })

1
Ha funzionato meglio nel mio contesto - nessun VFL però.
brainray

mi dà un avvertimento:Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tableview cell's content view. We're considering the collapse unintentional and using standard height instead.
Tapas Pal

10

Assolutamente possibile è stato in grado di farlo in questo modo:

[NSLayoutConstraint constraintsWithVisualFormat:@"H:[view]-(<=1)-[subview]"
                                        options:NSLayoutFormatAlignAllCenterY
                                        metrics:nil
                                          views:NSDictionaryOfVariableBindings(view, subview)];

Ho imparato questo da qui. Il codice sopra centra la sottoview in vista di Y ovviamente.

Swift3:

        NSLayoutConstraint.constraints(withVisualFormat: "H:[view]-(<=1)-[subview]",
                                       options: .alignAllCenterY,
                                                       metrics: nil,
                                                       views: ["view":self, "subview":_spinnerView])

7

Aggiungi il codice qui sotto e posiziona il pulsante al centro dello schermo. Assolutamente possibile

[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"H:|-[button]-|"
                           options:NSLayoutFormatAlignAllCenterY
                           metrics:0
                           views:views]];

[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"V:|-[button]-|"
                           options:NSLayoutFormatAlignAllCenterX
                           metrics:0
                           views:views]];

1

So che non vuoi che tu voglia, ma puoi ovviamente calcolare i margini e usarli per creare la stringa di formato visivo;)

Comunque no. Sfortunatamente non è possibile farlo "automaticamente" con VFL - almeno non ancora.


Bene, puoi usare NSString stringWithFormat: per creare dinamicamente la tua stringa VFL in fase di esecuzione.
TRVD1707,

1

Grazie alla risposta di @Evgenii, creo un esempio completo in sintesi:

centrare il quadrato rosso verticalmente con altezza personalizzata

https://gist.github.com/yallenh/9fc2104b719742ae51384ed95dcaf626

Puoi centrare una vista in verticale usando VFL (che non è del tutto intuitivo) o usa i vincoli manualmente. A proposito, non posso combinare sia centroY che altezza insieme in VFL come:

"H:[subView]-(<=1)-[followIcon(==customHeight)]"

Nella soluzione attuale i vincoli VFL vengono aggiunti separatamente.


0

Quello che deve essere fatto è che la superview dovrebbe essere dichiarata nel dizionario. Invece di usare |tu usi@{@"super": view.superview}; .

E NSLayoutFormatAlignAllCenterXper verticale e NSLayoutFormatAlignAllCenterYper orizzontale come opzioni.


0

Puoi usare viste extra.

NSDictionary *formats =
@{
  @"H:|[centerXView]|": @0,
  @"V:|[centerYView]|": @0,
  @"H:[centerYView(0)]-(>=0)-[yourView]": @(NSLayoutFormatAlignAllCenterY),
  @"V:[centerXView(0)]-(>=0)-[yourView]": @(NSLayoutFormatAlignAllCenterX),
};
NSDictionary *views = @{
  @"centerXView": centerXView, 
  @"centerYView": centerYView, 
  @"yourView": yourView,
};
 ^(NSString *format, NSNumber *options, BOOL *stop) {
     [superview addConstraints:
      [NSLayoutConstraint constraintsWithVisualFormat:format
                                              options:options.integerValue
                                              metrics:nil
                                                views:views]];
 }];

Se lo vuoi pulito, allora centerXView.hidden = centerYView.hidden = YES; .

PS: Non sono sicuro di poter chiamare le viste extra "segnaposto", perché l'inglese è la mia seconda lingua. Sarà apprezzato se qualcuno me lo può dire.


Qui manca qualcosa. Come si chiama il blocco sotto la dichiarazione delle viste ? Non è un codice legale nel modo in cui lo metti
ishahak,

0

Per coloro che sono venuti qui per una Interface Buildersoluzione basata (Google mi guida qui), basta aggiungere vincoli const larghezza / altezza e selezionare la sottoview -> control trascina nella sua superview -> selezionare "centra verticalmente / orizzontalmente in superview".


0

Questo è il mio metodo

//create a 100*100 rect in the center of superView
var consts = NSLayoutConstraint.constraints(withVisualFormat: "H:|-space-[myView]-space-|", options: [], metrics:["space":view.bounds.width/2-50], views:["myView":myView])

consts += NSLayoutConstraint.constraints(withVisualFormat: "V:|-space-[myView]-space-|", options: [], metrics: ["space":view.bounds.height/2-50], views: ["myView":myView])

-1

Devo solo dire che puoi usare questo invece di vincoli:

child.center = [parent convertPoint:parent.center fromView:parent.superview];

4
Devo solo dire che non è quello che sta chiedendo, né funzionerà se si ruota il dispositivo, si ridimensiona lo schermo, ecc.
jeffjv

-1

@ Risposta igor-muzyka in Swift 2 :

NSLayoutConstraint.constraintsWithVisualFormat("H:[view]-(<=1)-[subview]", 
                                               options: .AlignAllCenterY,
                                               metrics: nil,
                                               views: ["view":view, "subview":subview])

-3

Invece di usare VFL, puoi farlo facilmente nel generatore di interfacce. Nello Storyboard principale, ctrl + clic sulla vista che vuoi centrare. Trascina sulla superview (tenendo premuto ctrl) e seleziona "Centra X" o "Centra Y". Nessun codice necessario

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.