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?

Christian Schnorr
la source

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.

larsaque
la source
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

Evgenii
la source
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() })
jqgsninimo
la source
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])
Igor Muzyka
la source
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]];
Pankaj Gaikar
la source
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.

Tobi
la source
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.

Allen Huang
la source
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.

Lagos
la source
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.

CopperCash
la source
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".

superarts.org
la source
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])
Calcifer
la source
-1

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

child.center = [parent convertPoint:parent.center fromView:parent.superview];
Nik Kov
la source
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])
Federico Zanetello
la source
-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.

leenyburger
la source