Quand puis-je activer / désactiver les contraintes de layout?

J'ai mis en place plusieurs sets de contraintes dans IB, et je voudrais basculer par programme entre eux en fonction de l'état. Il y a une collection de sorties de constraintsA toutes marquées comme installées à partir de IB, et une collection de sorties de constraintsB qui sont toutes désinstallées dans IB.

Je peux basculer par programme entre les deux sets comme suit:

 NSLayoutConstraint.deactivateConstraints(constraintsA) NSLayoutConstraint.activateConstraints(constraintsB) 

Mais … je ne peux pas savoir quand faire ça. Il semble que je devrais pouvoir le faire une fois dans viewDidLoad , mais je ne peux pas le faire fonctionner. J'ai essayé d'appeler view.updateConstraints() et view.layoutSubviews() après avoir défini les contraintes, mais en vain.

J'ai trouvé que si je définis les contraintes dans viewDidLayoutSubviews tout fonctionne comme prévu. Je suppose que j'aimerais savoir deux choses …

  1. Pourquoi ai-je ce comportement?
  2. Est-il possible d'activer / désactiver les contraintes de viewDidLoad?

J'active et désactive NSLayoutConstraints dans viewDidLoad , et je n'ai aucun problème avec ça. Donc ça marche. Il doit y avoir une différence de configuration entre votre application et la mienne 🙂

Je vais juste décrire ma configuration – peut-être que cela peut vous donner une idée:

  1. J'ai mis en place @IBOutlets pour toutes les contraintes dont j'ai besoin pour activer / désactiver.
  2. Dans le ViewController , je sauvegarde les contraintes dans les propriétés de class qui ne sont pas faibles. La raison en est que j'ai trouvé qu'après avoir désactivé une contrainte, je ne pouvais pas la réactiver – c'était nul. Donc, il semble être supprimé lorsqu'il est désactivé.
  3. Je n'utilise pas NSLayoutConstraint.deactivate/activate comme vous, j'utilise constraint.active = YES / NO place.
  4. Après avoir défini les contraintes, j'appelle view.layoutIfNeeded() .
 override func viewDidLayoutSubviews() { // do it here, after constraints have been materialized } 

Peut-être pourriez-vous vérifier vos @properties , replace weak par strong .

Parfois, parce que active = NO définissez self.yourContraint = nil , de sorte que vous ne puissiez plus utiliser self.yourContraint .

Je crois que le problème que vous éprouvez est dû aux contraintes n'étant pas ajoutées à leurs vues jusqu'à viewDidLoad() est appelé. Vous avez un certain nombre d'options:

A) Vous pouvez connecter vos contraintes de disposition à un IBOutlet et y accéder dans votre code par ces references. Puisque les sockets sont connectées avant le viewDidLoad() , les contraintes doivent être accessibles et vous pouvez continuer à les activer et les désactiver.

B) Si vous souhaitez utiliser la fonction constraints() UIView pour accéder aux différentes contraintes, vous devez attendre que viewDidLayoutSubviews() démarre et le fasse, car c'est le premier point après avoir créé un controller de vue à partir d'une plume qu'il aura toutes les contraintes installées. N'oubliez pas d'appeler layoutIfNeeded() lorsque vous avez terminé. Cela présente l'inconvénient que la passe de layout sera effectuée deux fois s'il y a des modifications à appliquer et vous devez vous assurer qu'il n'y a aucune possibilité qu'une boucle infinie soit déclenchée.

Un petit mot d'avertissement: les contraintes désactivées ne sont pas returnnées par la méthode constraints() ! Cela signifie que si vous désactivez une contrainte avec l'intention de la réactiver plus tard, vous devrez garder une reference à celle-ci.

C) Vous pouvez oublier l'approche de storyboard et append vos contraintes manuellement à la place. Puisque vous faites cela dans viewDidLoad() je suppose que l'intention est de ne le faire qu'une fois pour toute la durée de vie de l'object plutôt que de changer la layout à la volée, donc cela devrait être une méthode acceptable.

Vous pouvez également ajuster la propriété priority pour "activer" et "les désactiver" (valeur 750 pour activer et 250 pour désactiver par exemple). Pour une raison quelconque, la modification du BOOL active n'a eu aucun effet sur mon interface user. Pas besoin de layoutIfNeeded et peut être défini et modifié à viewDidLoad ou à tout moment après cela.

Le bon moment pour désactiver les contraintes inutilisées:

 -(void)viewWillLayoutSubviews{ [super viewWillLayoutSubviews]; self.myLittleConstraint.active = NO; } 

Gardez à l'esprit que viewWillLayoutSubviews pourrait être appelé plusieurs fois, donc pas de calculs lourds ici, d'accord?

Note: si vous voulez ré-activer certaines contraintes plus tard, gardez-y toujours strong reference strong .

J'ai trouvé aussi longtime que vous avez défini les contraintes normales dans le rlocation de - (void)updateConstraints (objective c), avec une reference strong pour l'initialité utilisée contraintes actives et inactives. Et ailleurs dans le cycle de vue désactiver et / ou activer ce dont vous avez besoin, puis en appelant layoutIfNeeded , vous ne devriez avoir aucun problème.

L'essentiel n'est pas de réutiliser constamment le rlocation de updateConstraints et de séparer les activations des contraintes, tant que vous appelez updateConstraint s après votre première initialisation et layout. Cela semble importer après cela où dans le cycle de vue.

Lors de la création d'une vue, les methods de cycle de vie suivantes sont appelées dans l'ordre:

  1. loadView
  2. viewDidLoad
  3. VoirWillAppear
  4. VoirWillLayoutSubregements
  5. viewDidLayoutSubregements
  6. viewDidAppear

Maintenant à vos questions.

  1. Pourquoi ai-je ce comportement?

Réponse: Parce que lorsque vous essayez de définir les contraintes sur les vues dans viewDidLoad la vue n'a pas ses limites, donc les contraintes ne peuvent pas être définies. C'est seulement après viewDidLayoutSubviews que les limites de la vue sont finalisées.

  1. Est-il possible d'activer / désactiver les contraintes de viewDidLoad?

Réponse: Non. Raison expliquée ci-dessus.