167 votes

AutoLayout avec UIViews cachés ?

J'ai l'impression que c'est un paradigme assez commun de montrer/cacher UIViews le plus souvent UILabels en fonction de la logique commerciale. Ma question est la suivante : quelle est la meilleure façon d'utiliser AutoLayout pour répondre aux vues cachées comme si leur cadre était 0x0. Voici un exemple d'une liste dynamique de 1 à 3 fonctionnalités.

Dynamic features list

Actuellement, il y a un espace de 10px entre le bouton et la dernière étiquette, qui ne peut évidemment pas glisser vers le haut lorsque l'étiquette est cachée. Pour l'instant, j'ai créé une sortie pour cette contrainte et je modifie la constante en fonction du nombre d'étiquettes que j'affiche. C'est évidemment un peu compliqué puisque j'utilise des valeurs constantes négatives pour faire glisser le bouton vers le haut au-dessus des cadres cachés. C'est également mauvais parce que la contrainte n'est pas liée à des éléments de mise en page réels, mais à des calculs statiques sournois basés sur des hauteurs/plages connues d'autres éléments, et que cela va à l'encontre de ce pour quoi AutoLayout a été conçu.

Je pourrais évidemment créer de nouvelles contraintes en fonction de mes étiquettes dynamiques, mais c'est beaucoup de microgestion et de verbosité pour essayer de réduire simplement quelques espaces blancs. Existe-t-il de meilleures approches ? Changer la taille du cadre 0,0 et laisser AutoLayout faire son travail sans manipuler les contraintes ? Supprimer complètement les vues ?

Honnêtement, la modification de la constante dans le contexte de la vue cachée ne nécessite qu'une seule ligne de code avec un calcul simple. Recréer de nouvelles contraintes avec constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant: semble si lourd.

Edit Feb 2018 : Voir la réponse de Ben avec UIStackView s

0 votes

Merci Ryan pour cette question. Je devenais fou de savoir comment faire dans les cas que vous avez demandés. Chaque fois que je cherche le tutoriel pour autolayout, la plupart d'entre eux disent de se référer au site de tutoriel raywenderlich que je trouve un peu difficile à comprendre.

236voto

Max MacLeod Points 8425

Ma préférence personnelle pour afficher/masquer les vues est de créer un IBOutlet avec la contrainte de largeur ou de hauteur appropriée.

Je mets ensuite à jour le constant à la valeur 0 pour cacher, ou quelle que soit la valeur qui devrait être pour montrer.

Le grand avantage de cette technique est que les contraintes relatives seront maintenues. Par exemple, disons que vous avez une vue A et une vue B avec un écart horizontal de x . Lorsque la largeur de la vue A constant est réglé sur 0.f alors la vue B se déplacera vers la gauche pour remplir cet espace.

Il n'est pas nécessaire d'ajouter ou de supprimer des contraintes, ce qui est une opération lourde. Il suffit de mettre à jour la valeur de la contrainte constant fera l'affaire.

0 votes

J'aime beaucoup cette réponse. En gros, si je comprends bien, il s'agit de définir des contraintes fixes de hauteur ou de poids, de créer un IBOutlet pour ces contraintes, et si cette vue est cachée, il suffit de passer à une constante de 0,f et le reste des éléments devrait se mettre en place ?

1 votes

Exactement. Tant que - dans cet exemple - vous positionnez la vue B à droite de la vue A en utilisant un espace horizontal. J'utilise cette technique en permanence et elle fonctionne très bien.

0 votes

@MaxMacLeod Et si l'étiquette est potentiellement multi-lignes ? Dans ce cas, une contrainte de hauteur ne serait pas appropriée, n'est-ce pas ?

99voto

Simple As Could Be Points 3906

La solution d'utiliser une constante 0 lorsqu'il est caché et une autre constante si vous le montrez à nouveau est fonctionnel, mais il n'est pas satisfaisant si votre contenu a une taille flexible. Vous devez mesurer votre contenu flexible et définir une constante en retour. Cette méthode n'est pas satisfaisante et pose des problèmes si le contenu change de taille en raison d'événements liés au serveur ou à l'interface utilisateur.

J'ai une meilleure solution.

L'idée est de définir la règle de hauteur 0 comme ayant une priorité élevée lorsque nous masquons l'élément afin qu'il n'occupe pas d'espace dans la mise en page automatique.

Voici comment faire :

1. mettre en place une largeur (ou une hauteur) de 0 dans le constructeur d'interface avec une faible priorité.

setting width 0 rule

Interface Builder ne signalera pas les conflits parce que la priorité est faible. Testez le comportement en hauteur en fixant temporairement la priorité à 999 (1000 est interdit de mutation programmatique, donc nous ne l'utiliserons pas). Le constructeur d'interface va probablement maintenant crier au sujet des contraintes conflictuelles. Vous pouvez les résoudre en fixant les priorités des objets liés à 900 ou plus.

2. Ajoutez une sortie pour pouvoir modifier la priorité de la contrainte de largeur dans le code :

outlet connection

3. Ajustez la priorité lorsque vous masquez votre élément :

cell.alertTimingView.hidden    = place.closingSoon != true
cell.alertTimingWidth.priority = place.closingSoon == true ? 250 : 999

86voto

Ben Packard Points 3441

UIStackView est probablement la meilleure solution pour iOS 9+. Non seulement elle gère la vue cachée, mais elle supprime également les espaces et les marges supplémentaires si elle est configurée correctement.

11voto

mitul marsonia Points 196

Dans ce cas, je fais correspondre la hauteur de l'étiquette de l'auteur à un IBOutlet approprié :

@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;

et lorsque je fixe la hauteur de la contrainte à 0,0f, nous conservons le "padding", car la hauteur du bouton Play le permet.

cell.authorLabelHeight.constant = 0; //Hide 
cell.authorLabelHeight.constant = 44; //Show

enter image description here

10voto

SeanR Points 313

Il y a beaucoup de solutions ici mais mon approche habituelle est encore différente :)

Mettez en place deux ensembles de contraintes similaires à la réponse de Jorge Arimany et de TMin :

enter image description here

Les trois contraintes marquées ont la même valeur pour la constante. Les contraintes marquées A1 et A2 ont leur Priorité fixée à 500, tandis que la contrainte marquée B a sa Priorité fixée à 250 (ou UILayoutProperty.defaultLow dans le code).

Reliez la contrainte B à un IBOutlet. Ensuite, lorsque vous masquez l'élément, il vous suffit de définir la priorité de la contrainte sur élevée (750) :

constraintB.priority = .defaultHigh

Mais lorsque l'élément est visible, remettez la priorité sur faible (250) :

constraintB.priority = .defaultLow

L'avantage (certes mineur) de cette approche par rapport à une simple modification isActive pour la contrainte B est que vous avez toujours une contrainte fonctionnelle si l'élément transitoire est retiré de la vue par d'autres moyens.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X