J'ai défini plusieurs ensembles de contraintes dans IB et je souhaite basculer par programme entre eux en fonction de l'état. Il existe une collection constraintsA
qui sont toutes marquées comme installées à partir d'IB et une collection constraintsB
qui sont toutes désinstallées dans IB.
Je peux par programme basculer entre les deux ensembles comme suit:
NSLayoutConstraint.deactivateConstraints(constraintsA)
NSLayoutConstraint.activateConstraints(constraintsB)
Mais ... je ne peux pas comprendre quand faire cela. Il semble que je devrais pouvoir le faire une fois dans viewDidLoad
, mais je ne parviens 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 constaté que si je définissais les contraintes dans viewDidLayoutSubviews
tout fonctionnait comme prévu. Je suppose que j'aimerais savoir deux choses ...
J'active et désactive NSLayoutConstraints
dans viewDidLoad
, et je n'ai aucun problème avec cela. 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 piste:
@IBOutlets
pour toutes les contraintes que je dois activer/désactiver.ViewController
, je sauvegarde les contraintes dans des propriétés de classe qui ne sont pas faibles. La raison en est que j’ai constaté qu’après la désactivation d’une contrainte, je ne pouvais plus la réactiver - c’était nul. Donc, il semble être supprimé lorsqu'il est désactivé.NSLayoutConstraint.deactivate/activate
comme vous, j'utilise plutôt constraint.active = YES
/NO
.view.layoutIfNeeded()
.Peut-être que vous pourriez vérifiez votre @properties
, remplacez weak
par strong
.
Parfois, c'est parce que active = NO
définit self.yourConstraint = nil
, de sorte que vous ne puissiez plus utiliser self.yourConstraint
.
override func viewDidLayoutSubviews() {
// do it here, after constraints have been materialized
}
Je pense que le problème que vous rencontrez est dû au fait que des contraintes n’ont pas été ajoutées à leurs vues jusqu’à ce que AFTER viewDidLoad()
soit appelé. Vous avez un certain nombre d'options:
A) Vous pouvez connecter vos contraintes de présentation à un IBOutlet et y accéder dans votre code par ces références. Étant donné que les prises sont connectées avant le début de viewDidLoad()
, les contraintes doivent être accessibles et vous pouvez continuer à les activer et les désactiver.
B) Si vous souhaitez utiliser la fonction constraints()
de UIView pour accéder aux diverses contraintes, vous devez attendre que viewDidLayoutSubviews()
démarre et le fasse ici, car c'est le premier point après créer un contrôleur de vue à partir d'une nib qui aura des contraintes installées. N'oubliez pas d'appeler layoutIfNeeded()
lorsque vous avez terminé. Cela présente l'inconvénient que la passe de présentation 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.
Petit mot d’avertissement: les contraintes désactivées ne sont PAS renvoyées par le signe constraints()
! Cela signifie que si vous désactivez une contrainte dans l’intention de la réactiver ultérieurement, vous devrez conserver une référence.
C) Vous pouvez oublier l'approche du storyboard et ajouter vos contraintes manuellement à la place. Puisque vous faites cela dans viewDidLoad()
, je suppose que l’intention est de ne le faire qu’une seule fois pendant toute la durée de vie de l’objet plutôt que de changer la disposition à la volée. C’est donc une méthode acceptable.
Vous pouvez également ajuster la propriété priority
pour les "activer" et les "désactiver" (valeur 750 à activer et 250 à désactiver par exemple). Pour une raison quelconque, le changement de active
BOOL n'a eu aucun effet sur mon interface utilisateur. Pas besoin de layoutIfNeeded
et peut être défini et modifié à viewDidLoad ou à tout moment par la suite.
Le bon moment pour désactiver les contraintes non utilisé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?
Remarque: si vous souhaitez réactiver certaines contraintes ultérieurement, stockez-les toujours strong
référence.
J'ai trouvé aussi longtemps que vous définissez les contraintes normales dans le remplacement de - (void)updateConstraints
(objectif C), avec une référence strong
pour l'initialité utilisée, des contraintes actives et non actives. Et ailleurs dans le cycle de visualisation, désactivez et/ou activez ce dont vous avez besoin, puis appelez layoutIfNeeded
et ne rencontrez aucun problème.
L'essentiel est de ne pas réutiliser constamment le remplacement de updateConstraints
et de séparer les activations des contraintes, tant que vous appelez updateConstraint
s après votre première initialisation et mise en page. Cela semble avoir de l'importance après cela, où dans le cycle de vue.
Lors de la création d'une vue, les méthodes de cycle de vie suivantes sont appelées dans l'ordre:
Maintenant à vos questions.
- Pourquoi ai-je ce comportement?
Réponse: étant donné que lorsque vous essayez de définir les contraintes sur les vues dans viewDidLoad
, la vue n'a pas de limites, vous ne pouvez donc pas définir de contraintes. Ce n'est qu'après viewDidLayoutSubviews
que les limites de la vue sont finalisées.
- Est-il possible d'activer/désactiver les contraintes de viewDidLoad?
Réponse: Non. Raison expliquée ci-dessus.