Autolayout avec sous-vues pouvant ne pas être présentes
muqaddar
Administrateur
Salut,
J'arrive à bien utiliser autolayout dans 90% des cas que je peux rencontrer.
Là , je voudrais connaà®tre la meilleure façon de gérer l'espace d'une vue par rapport à une autre sachant que l'autre peut ne pas être présente.
Dessin:
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Avec autolayout, on ne doit pas modifier la taille des frames - jamais ! ::)
Tu devrais avoir une contrainte entre la droite de B et sa vue contenant. Il faut la connecter à un IBOutlet dans ton contrôleur et modifier le constante de cette contrainte afin que B se déplace au droit selon la valeur (peut-être negative) de la constante.
Donc je dirai, soit tu crées un IBOutlet de ta contrainte sur ta vue A pour en avoir une référence et la modifier par la suite. Soit tu crées la contrainte par le code et tu la configure dans le viewDidLoad.
Dans tous les cas avec Auto-Layout tu ne peux plus toucher aux frames des objets. Je pense que tu le sais mais un peu de rappel ne fait pas de mal, pour les déplacer dans la vue, il faut utiliser -updateConstraint et configurer la taille des contraintes que tu souhaites, avec maContrainte.constant = x. (x pouvant être négatif ou positif). Tu peux aussi appeler [self updateConstraint] à tout moment dans ton code, par exemple si tu souhaites une position particulière sous certains critères.
Dans ton cas j'ajouterai la vue et la contrainte dans le viewDidLoad, ça devrait te donner un meilleur contrôle du comportement.
Après je suis bien preneur d'autres solutions ! Tiens nous au courant.
Je n'ai pas vérifié mais je me demande s'il n'est pas possible de définir deux contraintes avec des priorités différentes, une avec B et une avec la vue contenante.
Pour moi tu as plusieurs solutions qui restent simples :
Solution 1) (La solution la plus simple à mon avis)
- Tu crées une contrainte par IB sur la largeur de ta vue B, et tu mets un IBOutlet dessus
- Quand tu veux cacher ta vue B, tu changes la propriété "constant" de cette contrainte pour la passer à 0
Gros avantage de cette solution, en plus d'être simple, c'est que tu peux aussi si ça t'intéresse animer ce changement de contrainte en seulement 1 ou 2 lignes de code. Ce qui te permet de donner un effet comme quoi la vue B se réduit en largeur jusqu'à disparaà®tre.
Solution 2)
- Tu crées 2 contraintes par IB, une entre la droite de A et la gauche de B, et une autre entre la droite de A et la droite de sa vue parente, et tu mets un IBOutlet strong sur chacune de ces 2 contraintes
- Au démarrage, tu en supprimes une des 2 (la 2ème si ta vue B est visible au démarrage par exemple) de sa vue, mais tu la gardes dans ta @property quand même
- Quand tu veux masquer ta vue B, du coup tu supprimes la 1ère contrainte et tu rajoutes la 2ème.
Le fait de les avoir créées dans IB et de les garder dans des "@property(strong) IBOutlet" t'évite d'avoir à écrire les contraintes par code et d'avoir à les recréer à chaque fois. Dans ton code la seule chose que tu as à faire c'est ajouter une contrainte et en supprimer une autre, mais tu n'as pas à t'embêter à les créer par code. Mais bon tu as à jongler entre 2 contraintes du coup.
Solution 3)
- Même chose que la solution 2, mais au lieu de supprimer une contrainte ou l'autre par code, tu changes leur priorité. Tu mets la contrainte 1 plus prioritaire que la contrainte 2 pour qu'elle prenne le pas sur cette dernière et basta
ça peut avoir l'effet de "coincer" les sous-vues et, si on a créé les contraintes de taille minimum ou d'espace minimum sur ces sous-vues, on pourrait arriver aux conflits.
Muqaddar, c'est quoi que tu veux qui se passe en termes de mouvements entre les trois vues ?
En effet. Mais dans ce cas on peut jouer sur les priorités des contraintes, mettre une priorité plus forte sur cette contrainte de largeur de vue B que les contraintes des sous-vues de la vue B.
Je viens de vous lire.
C'est passionnant.
Les animations ne sont pas obligatoires mais ça ferait un plus sympa.
Pour les contraintes des sous-vues, et pour info:
- la vue A est une scrollView qui contient une subview.
- la vue B est une UITableView
mais aussi:
- la vue A a une largeur dynamique
- la vue B a une largeur fixe (genre 100 pixels)
Enfin, la superview s'étire dans tous les sens en mode portrait et paysage.
Je sais quand j'ai besoin d'afficher la vue B dans une méthode "reloadData" de mon VC qui fait 1 petit calcul sur mes données.
Je vais la tester sous 2 ou 3 jours et je reviens vers vous pour vous dire ce qu'il en est.
Merci à tous.
Bon, je débute avec la programmation des contraintes.
J'ai fait ça, sans succès, pour passer la largeur de ma tableView à 0 (rappel: tableLayoutContraint est connecté avec un outlet sur la contrainte dans IB):
Le code est lu, mais rien ne change.
J'ai également essayé de la placer dans:
mais rien n'a changé.
C'est un peu comme si tu avais un IBOutlet sur une UIView dans ton XIB et que pour changer la frame de ta vue tu faisais self.monoutlet = [[UIView alloc] initWithFrame:newFrame] (donc créait une nouvelle vue, que tu oublierais en plus d'ajouter en subview de sa parente) au lieu de faire self.monoutlet.frame = newFrame; comme il se doit.
Si tu as déjà un IBOutlet sur une contrainte existante et déjà placée dans ton XIB faut pas en instancier une autre, il faut changer l'existante. self.tableLayoutConstraint.constant = 0; fera donc l'affaire.
Je suis entièrement d'accord avec toi et la logique des choses.
Sauf qu'en lisant la doc de NSLayoutConstraint, je n'avais pas compris que je pouvais passer directement par cette propriété, et le fait que tous les exemples de code lus sur le net utilisent la méthode "constraintWithItem" m'a fait croire à cette hypothèse.
Wouah, ça marche beaucoup beaucoup mieux... :-)
Y'a plus qu'à l'animer.
Où parentView est la vue sur laquelle la contrainte est ajoutée, ce qui est généralement le parent commun aux 2 vues entre lesquelles tu as mis ta contrainte (l'ancêtre commun dans la hiérarchie de vue qui contient à la fois ta vue A et ta vue B.)
Tu peux aussi mettre la ligne constraint.constant dans le block d'animation, si cela te paraà®t plus logique/lisible (pour faire le parallèle avec ce que tu ferais si tu voulais animer une autre propriété, comme la frame d'une vue quand on n'utilise pas AutoLayout, etc, où tu mettrais le code dans le bloc d'animation) : les 2 fonctionnent.
Le secret est surtout d'appeler layoutIfNeeded dans le block d'animation pour que le changement dans la contrainte soit animé, car juste changer la constante dans le bloc d'animation ne suffira pas.
---
PS : l'appel à setNeedsUpdateConstraints n'est pas obligatoire (dans le sens où ça va souvent marcher même si tu ne le mets pas) mais cela permet de laisser une chance aux subviews d'exécuter leur code updateConstraints dans le cas où certaines subviews auraient surchargé cette méthode (l'implémentation par défaut de updateConstraints est vide, donc si aucune subview ne surcharge cette méthode, appeler setNeedsUpdateConstraints n'a finalement aucun effet, mais autant l'appeler et mettre du code sûr pour que ça continue de marcher le jour où tu utiliserais une subview dont l'implémentation de updateConstraints aurait été surchargée)
Voilà , c'est ce que j'avais remarqué hier soir avant que tu postes.
setNeedsUpdateConstraints n'avait pas d'incidence pour mon cas, mais je vais le rajouter quand-même.
J'avais fait ça:
Perso je l'ai jamais mis dans mes codes et j'ai jamais eu de soucis mais bon.
Merci à vous pour les conseils !
J'ai bien avancé grâce à vous.
Un petit extrait (vous voyez pas où je clique mais c'est pas grave) :
Bon bon bon...
AutoLayout, c'est sympa, mais ca fait des noeuds au cerveau parfois !
Alors, j'ai un peu la même question que Muqua mais en vertical...
J'ai une vue X qui comporte des sous vues. Disons, un titre, une image pour faire court.
Le titre a une contrainte vertical spacing avec X et avec l'image qui est en dessous.
L'image a une contrainte avec le bottom de X, ce qui permet d'avoir une vue X avec la bonne hauteur.
Maintenant, je veux masquer cette vue...
Je fais comment ?
J'ai essayé d'ajouter une contrainte de hauteur à X en la mettant <= valeur max (car l'image peut varier de hauteur par ex) et lorsque je veux masquer la vue, je passe cette contrainte à 0.
Mais rien ne bouge
Bon laissez tomber. Avec la contrainte Height que je passe à 0, ça fonctionne.
Faut juste utiliser la bonne outlet...
Avec la bonne outlet, ça fonctionne. Seulement voilà .
Dans ma vue que je veux masquer, il y a des sous-vues, elles-mêmes agencées via des contraintes.
Il y a un label, dessous, un séparateur et dessous, une tableView.
Si ma vue doit être masquée, je passe sa contrainte de hauteur à 0 et je la passe en hidden.
Seulement voilà , Dans ce cas précis (celui avec la tableView), je me retrouve avec des warnings qui me disent qu'XCode est obligé de supprimer des contraintes car en passant à 0, il n'arrive plus à ttes les satisfaire...
Faut il s'embêter à tout passer à 0 à l'intérieur ?
J'ai même fait un :
Mais rien n'y fait. Le warning apparait quand meme...
Unable to simultaneously satisfy constraints.
Je continue mon long monologue de l'Autolayout (à défaut de vagin ) :
=> il faut que la contrainte de hauteur de la vue à masquer ait une priority supérieure aux contraintes de ses sous vues.
Un jour sans doute, j'aimerai Autolayout
J'ai fait quelque chose comme ci :
1. La sous-vue avec ses deux contraintes vers la super-vue et le label
2. Une contrainte entre la super-vue et la sous-vue
J'enlève la sous-vue (ses contraintes sont automatiquement enlevés)
J'ajoute la contrainte entre la super-vue et le label
Ou l'inverse.
Voilà , c'est ça. C'est ce que j'ai fait et ça fonctionne.
Tu as vu ces docs : https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html#//apple_ref/doc/uid/TP40010853-CH5-SW1 ?
Merci Joanna. Non mais je trouve très bien Autolayout.
C'est juste que je suis sur un écran avec des listes dont le nombre n'est pas connu a priori, parfois y a rien, etc...
Et dans ce cas, c'est un peu compliqué.
Par exemple, j'ai une box Topos dans laquelle, je vais pouvoir avoir :
=> 0 à n topos guides papiers pour lesquels je vais présenter la couverture (image) avec le titre et l'auteur
=> 0 à n liens de topos sur internet
=> 0 à n liens de sites web qui en parlent
=> 0 à n topos téléchargeables sur ClimbingAway
Je me demande si j'aurai pas intérêt à utiliser une WebView parfois...