J'ai l'impression que c'est un paradigme assez courant de montrer / cacher UIViews
, le plus souvent UILabels
, en fonction de la logique métier. Ma question est de savoir 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 de liste dynamique de 1 à 3 fonctionnalités.
À l'heure actuelle, j'ai un espace supérieur de 10 pixels entre le bouton et la dernière étiquette, qui ne glissera évidemment pas vers le haut lorsque l'étiquette est masquée. À partir de maintenant, j'ai créé une sortie pour cette contrainte et en modifiant la constante en fonction du nombre d'étiquettes que j'affiche. C'est évidemment un peu piraté car j'utilise des valeurs constantes négatives pour pousser le bouton vers le haut sur les cadres cachés. C'est également mauvais car il n'est pas limité aux éléments de mise en page réels, juste des calculs statiques sournois basés sur des hauteurs / rembourrages connus d'autres éléments, et en luttant évidemment contre ce pour quoi AutoLayout a été conçu.
Je pourrais évidemment simplement créer de nouvelles contraintes en fonction de mes étiquettes dynamiques, mais c'est beaucoup de microgestion et beaucoup de verbosité pour essayer de simplement réduire certains espaces. Existe-t-il de meilleures approches? Changer la taille de l'image 0,0 et laisser AutoLayout faire son travail sans manipulation des contraintes? Supprimer complètement les vues?
Honnêtement, la simple modification de la constante à partir du contexte de la vue masquée nécessite 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 Février 2018 : Voir la réponse de Ben avec UIStackView
s
la source
Réponses:
UIStackView
est probablement la voie à suivre pour iOS 9+. Non seulement il gère la vue masquée, mais il supprime également l'espacement et les marges supplémentaires s'il est configuré correctement.la source
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 la
constant
valeur à0
cacher, ou quelle que soit la valeur à afficher.Le gros avantage de cette technique est que les contraintes relatives seront maintenues. Par exemple, disons que vous avez la vue A et la vue B avec un écart horizontal de x . Lorsque la largeur de la vue A
constant
est définie sur, la0.f
vue B se déplace 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. La simple mise à jour des contraintes
constant
fera l'affaire.la source
La solution d'utiliser une constante
0
lorsqu'elle est masquée et une autre constante si vous la montrez à nouveau est fonctionnelle, mais elle n'est pas satisfaisante si votre contenu a une taille flexible. Vous auriez besoin de mesurer votre contenu flexible et de définir un recul constant. Cela semble faux et pose des problèmes si le contenu change de taille en raison d'événements du serveur ou de l'interface utilisateur.J'ai une meilleure solution.
L'idée est de définir la règle de hauteur a 0 pour avoir une priorité élevée lorsque nous masquons l'élément afin qu'il ne prenne aucun espace de mise en page automatique.
Voici comment procéder:
1. définissez une largeur (ou hauteur) de 0 dans le générateur d'interface avec une faible priorité.
Interface Builder ne crie pas sur les conflits car la priorité est faible. Testez le comportement de la hauteur en définissant temporairement la priorité sur 999 (1000 est interdit de muter par programme, nous ne l'utilisons donc pas). Le constructeur d'interface va probablement crier maintenant sur les contraintes conflictuelles. Vous pouvez résoudre ces problèmes en définissant des priorités sur les objets associés à environ 900.
2. Ajoutez une prise pour pouvoir modifier la priorité de la contrainte de largeur dans le code:
3. Ajustez la priorité lorsque vous masquez votre élément:
la source
Dans ce cas, je mappe la hauteur de l'étiquette Auteur à un IBOutlet approprié:
et quand je règle la hauteur de la contrainte à 0.0f, nous conservons le "padding", car la hauteur du bouton Play le permet.
la source
Il y a beaucoup de solutions ici mais mon approche habituelle est encore différente :)
Configurez deux ensembles de contraintes similaires à la réponse de Jorge Arimany et TMin:
Les trois contraintes marquées ont la même valeur pour la constante. Les contraintes marquées A1 et A2 ont leur priorité définie sur 500, tandis que la contrainte marquée B a sa priorité définie sur 250 (ou
UILayoutProperty.defaultLow
dans le code).Connectez la contrainte B à un IBOutlet. Ensuite, lorsque vous masquez l'élément, il vous suffit de définir la priorité de la contrainte sur haute (750):
constraintB.priority = .defaultHigh
Mais lorsque l'élément est visible, redéfinissez la priorité sur faible (250):
constraintB.priority = .defaultLow
L'avantage (certes mineur) de cette approche par rapport au simple changement
isActive
de la contrainte B est que vous avez toujours une contrainte de travail si l'élément transitoire est supprimé de la vue par d'autres moyens.la source
Sous-classez la vue et remplacez-la
func intrinsicContentSize() -> CGSize
. Revenez simplementCGSizeZero
si la vue est masquée.la source
invalidateIntrinsicContentSize
. Vous pouvez le faire dans un remplacementsetHidden
.Je viens de découvrir que pour qu'un UILabel ne prenne pas d'espace, vous devez le cacher ET définir son texte sur une chaîne vide. (iOS 9)
Connaître ce fait / bogue pourrait aider certaines personnes à simplifier leur mise en page, peut-être même celle de la question d'origine, alors j'ai pensé que je la posterais.
la source
Je suis surpris qu'il n'y ait pas d'approche plus élégante
UIKit
pour ce comportement souhaité. Il semble être une chose très courante de vouloir pouvoir faire.Depuis la connexion des contraintes à
IBOutlets
et la définition de leurs constantes sur une0
sensation dégueulasse (et ayant provoqué desNSLayoutConstraint
avertissements lorsque votre vue avait des sous-vues), j'ai décidé de créer une extension qui donne une approche simple et dynamique pour masquer / afficher unUIView
qui a des contraintes de mise en page automatiqueIl masque simplement la vue et supprime les contraintes extérieures. Lorsque vous affichez à nouveau la vue, elle rajoute les contraintes. Le seul inconvénient est que vous devrez spécifier des contraintes de basculement flexibles pour les vues environnantes.
Modifier Cette réponse est destinée à iOS 8.4 et inférieurs. Dans iOS 9, utilisez simplement l'
UIStackView
approche.la source
La meilleure pratique consiste, une fois que tout a les bonnes contraintes de mise en page, ajouter une hauteur ou avec une contrainte, selon la façon dont vous voulez que les vues environnantes se déplacent et connectent la contrainte dans une
IBOutlet
propriété.Assurez-vous que vos propriétés sont
strong
dans le code, il vous suffit de définir la constante sur 0 et de l' activer , puis de masquer le contenu ou de le désactiver pour afficher le contenu. C'est mieux que de gâcher la valeur constante et de la restaurer. N'oubliez pas d'appeler
layoutIfNeeded
ensuite.Si le contenu à masquer est groupé, la meilleure pratique consiste à tout mettre dans une vue et à ajouter les contraintes à cette vue
Une fois que vous avez votre configuration, vous pouvez la tester dans IntefaceBuilder en définissant votre contrainte sur 0, puis en revenant à la valeur d'origine. N'oubliez pas de vérifier les priorités des autres contraintes pour éviter tout conflit lorsqu'elles sont cachées. une autre façon de le tester est de le mettre à 0 et de définir la priorité sur 0, mais vous ne devez pas oublier de le restaurer à la priorité la plus élevée.
la source
Je crée une catégorie pour mettre à jour facilement les contraintes:
Réponse ici: Hide autolayout UIView: Comment obtenir NSLayoutConstraint existant pour mettre à jour celui-ci
la source
Ma méthode préférée est très similaire à celle suggérée par Jorge Arimany.
Je préfère créer de multiples contraintes. Créez d'abord vos contraintes lorsque la deuxième étiquette est visible. Créez une sortie pour la contrainte entre le bouton et la deuxième étiquette (si vous utilisez objc, assurez-vous qu'elle est forte). Cette contrainte détermine la hauteur entre le bouton et la deuxième étiquette lorsqu'elle est visible.
Créez ensuite une autre contrainte qui spécifie la hauteur entre le bouton et l'étiquette du haut lorsque le deuxième bouton est masqué. Créez une prise pour la deuxième contrainte et assurez-vous que cette prise a un pointeur fort. Décochez ensuite la
installed
case dans le générateur d'interface et assurez-vous que la priorité de la première contrainte est inférieure à cette deuxième priorité de contraintes.Enfin, lorsque vous masquez la deuxième étiquette, basculez la
.isActive
propriété de ces contraintes et appelezsetNeedsDisplay()
Et c'est tout, pas de nombres Magic, pas de mathématiques, si vous avez plusieurs contraintes à activer et à désactiver, vous pouvez même utiliser des collections de points de vente pour les organiser par état. (AKA conserve toutes les contraintes cachées dans un OutletCollection et les non-cachées dans une autre et itère simplement sur chaque collection en basculant leur statut .isActive).
Je sais que Ryan Romanchuk a dit qu'il ne voulait pas utiliser de contraintes multiples, mais j'ai l'impression que ce n'est pas de la microgestion, et qu'il est plus simple que de créer dynamiquement des vues et des contraintes par programme (ce que je pense qu'il voulait éviter si je Je lis bien la question).
J'ai créé un exemple simple, j'espère que c'est utile ...
la source
Je vais également fournir ma solution, pour offrir de la variété.) Je pense que créer une sortie pour la largeur / hauteur de chaque élément plus l'espacement est tout simplement ridicule et fait exploser le code, les erreurs possibles et le nombre de complications.
Ma méthode supprime toutes les vues (dans mon cas, les instances UIImageView), sélectionne celles qui doivent être rajoutées et, dans une boucle, elle les rajoute chacune et crée de nouvelles contraintes. C'est en fait très simple, veuillez suivre. Voici mon code rapide et sale pour le faire:
J'obtiens une disposition et un espacement propres et cohérents. Mon code utilise Masonry, je le recommande fortement: https://github.com/SnapKit/Masonry
la source
Essayez BoxView , il rend la mise en page dynamique concise et lisible.
Dans votre cas c'est:
la source