Centrage d'une vue dans sa supervision à l'aide du langage de format visuel


160

Je viens de commencer à apprendre AutoLayout pour iOS et j'ai jeté un coup d'œil à Visual Format Language.

Tout fonctionne bien, sauf une chose: je ne peux tout simplement pas avoir une vue au centre de sa supervision.
Est-ce possible avec VFL ou dois-je créer moi-même une contrainte manuellement?

Réponses:


232

Actuellement, non, il ne semble pas possible de centrer une vue dans la supervision en utilisant uniquement VFL. Il n'est cependant pas si difficile de le faire en utilisant une seule chaîne VFL et une seule contrainte supplémentaire (par axe):

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

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

On pourrait penser que vous seriez simplement capable de faire ceci (c'est ce que j'ai d'abord pensé et essayé quand j'ai vu cette question):

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

J'ai essayé de nombreuses variantes différentes de ce qui précède en essayant de le plier à ma volonté, mais cela ne semble pas s'appliquer à la supervision, même lorsque vous avez explicitement deux chaînes VFL séparées pour les deux axes ( H:|V:). Je me suis alors commencé à essayer et d' isoler exactement quand les options ne s'appliquent à la VFL. Ils semblent ne pas s'appliquer à la supervision dans le VFL et ne s'appliqueront qu'aux vues explicites mentionnées dans la chaîne VFL (ce qui est décevant dans certains cas).

J'espère qu'à l'avenir, Apple ajoutera une sorte de nouvelle option pour que les options VFL prennent en compte la supervision, même si elle ne le fait que lorsqu'il n'y a qu'une seule vue explicite en plus de la vue dans la VFL. Une autre solution pourrait être une autre option passée dans le VFL qui dit quelque chose comme: NSLayoutFormatOptionIncludeSuperview.

Inutile de dire que j'ai beaucoup appris sur VFL en essayant de répondre à cette question.


2
Merci pour cette réponse détaillée. La seule raison pour laquelle j'utilise VFL, cependant, est de me débarrasser de ces vilains morceaux de contraintes. Dommage qu'Apple ne supporte pas encore cela ...
Christian Schnorr

@larsacus Pourriez-vous expliquer pourquoi la réponse ci-dessous fonctionne? Cela me rend perplexe.
Matt G

8
La réponse d'Evgeny ci-dessous fonctionne parce que le moteur de mise en page automatique traite |et [superview]comme des entités internes distinctes même si elles représentent le même objet sémantique. En utilisant [superview], la mise en page automatique inclut l'objet superview dans ses métriques d'alignement (dans le cas de sa réponse au lien github, c'est la métrique NSLayoutFormatAlignAllCenterY/ X).
larsacus

@larsacus Génial, merci!
Matt G

Savons-nous si la limitation s'applique toujours avec la version de VFL livrée avec iOS 7.x actuel?
Drux

138

Oui, il est possible de centrer une vue dans sa supervision avec Visual Format Language. À la fois verticalement et horizontalement. Voici la démo:

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


15
C'est génial, pensez-vous que vous pourriez développer cela et expliquer pourquoi cela fonctionne?
tapi

3
J'ai fini par utiliser quelque chose de plus proche de la réponse marquée, mais +1 pour une pièce jointe github dans la vôtre
Rembrandt Q. Einstein

4
@Dan Si cela ne fonctionne pas pour vous, vous devrez peut-être également mettre en place des contraintes pour la largeur et la hauteur. Le VFL serait comme ceci pour la largeur: @ "[label (== 200)]" et comme ceci pour la hauteur: @ "V: [label (== 200)]"
Jonathan Zhan

1
Pourquoi les résultats de | et [superview] différent? Est-ce quelque chose qui devrait être radar ou y a-t-il un problème que je ne suis tout simplement pas?
Matt G

3
Impossible d'analyser le format de contrainte: le masque d'options nécessitait que les vues soient alignées sur un bord horizontal, ce qui n'est pas autorisé pour une mise en page également horizontale. H: [superview] - (<= 1) - [label1]
grabner

82

Je pense qu'il vaut mieux créer manuellement des contraintes.

[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]];

Maintenant, nous pouvons utiliser NSLayoutAnchor pour définir par programme AutoLayout:

(Disponible dans iOS 9.0 et versions ultérieures)

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

Je recommande d'utiliser SnapKit , qui est un DSL pour faciliter la mise en page automatique sur iOS et macOS.

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

1
A fonctionné mieux dans mon contexte - pas de VFL cependant.
brainray

ça me donne un avertissement: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

Absolument possible a pu le faire comme ceci:

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

J'ai appris cela d' ici. Le code ci-dessus centre évidemment la sous-vue par Y.

Swift3:

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

7

Ajoutez le code ci-dessous et placez votre bouton au centre de l'écran. Absolument possible.

[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

Je sais que ce n'est pas ce que vous voulez mais vous pouvez bien sûr calculer les marges et les utiliser pour créer la chaîne de format visuel;)

Bref, non. Malheureusement, il n'est pas possible de faire cela «automatiquement» avec VFL - du moins pas encore.


Eh bien, vous pouvez utiliser NSString stringWithFormat: pour créer votre chaîne VFL de manière dynamique au moment de l'exécution.
TRVD1707

1

Grâce à la réponse de @Evgenii, je crée un exemple complet dans l'essentiel:

centrer le carré rouge verticalement avec une hauteur personnalisée

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

Vous pouvez centrer une vue verticalement en utilisant soit VFL (ce qui n'est pas tout à fait intuitif) ou utiliser des contraintes manuellement. À propos, je ne peux pas combiner à la fois centerY et height en VFL comme:

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

Dans la solution actuelle, les contraintes VFL sont ajoutées séparément.


0

Ce qu'il faut faire, c'est que superview doit être déclaré dans le dictionnaire. Au lieu d'utiliser, |vous utilisez @{@"super": view.superview};.

Et NSLayoutFormatAlignAllCenterXpour vertical et NSLayoutFormatAlignAllCenterYpour horizontal en option.


0

Vous pouvez utiliser des vues supplémentaires.

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]];
 }];

Si vous le voulez bien, alors centerXView.hidden = centerYView.hidden = YES; .

PS: Je ne suis pas sûr de pouvoir appeler les vues supplémentaires des "espaces réservés", car l'anglais est ma deuxième langue. Ce sera apprécié si quelqu'un peut me le dire.


Quelque chose manque ici. Comment appelez-vous le bloc sous la déclaration de vues ? Ce n'est pas un code juridique tel que vous le
dites

0

Pour ceux qui sont venus ici pour une Interface Buildersolution basée (Google me conduit ici), ajoutez simplement des contraintes de largeur / hauteur const, et sélectionnez la sous-vue -> faites glisser le contrôle vers sa supervision -> sélectionnez "centrer verticalement / horizontalement dans superview".


0

C'est ma méthode

//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

Je dois juste dire que vous pouvez utiliser ceci au lieu de contraintes:

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

4
Je dois juste dire que ce n'est pas ce qu'il demande et que cela ne fonctionnera pas si vous faites pivoter l'appareil, que l'écran est redimensionné, etc.
jeffjv

-1

Réponse @ igor-muzyka dans Swift 2 :

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

-3

Au lieu d'utiliser VFL, vous pouvez facilement le faire dans le générateur d'interface. Dans le Storyboard principal, ctrl + clic sur la vue que vous souhaitez centrer. Faites glisser vers la vue supervisée (tout en maintenant la touche Ctrl enfoncée) et sélectionnez "Centre X" ou "Centre Y". Aucun code nécessaire.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.