Afficher les changements de cadre entre viewWillAppear: et viewDidAppear:

85

J'ai découvert un comportement étrange dans mon application, où un connecté IBOutleta son cadre de vue connecté entre les appels dans mon contrôleur de vue à viewWillAppear:et viewDidAppear:. Voici le code pertinent dans ma UIViewControllersous - classe:

-(void)viewWillAppear:(BOOL)animated {
    NSLog(@"%@", self.scrollView);
}

-(void)viewDidAppear:(BOOL)animated {
    NSLog(@"%@", self.scrollView);
}

et la sortie de journal résultante:

MyApp[61880:c07] <UIScrollView: 0x1057eff0; frame = (0 0; 0 0); clipsToBounds = YES; autoresize = TM+BM; gestureRecognizers = <NSArray: 0x10580100>; layer = <CALayer: 0x1057f210>; contentOffset: {0, 0}>
MyApp[61880:c07] <UIScrollView: 0x1057eff0; frame = (0 44; 320 416); clipsToBounds = YES; autoresize = TM+BM; gestureRecognizers = <NSArray: 0x10580100>; layer = <CALayer: 0x1057f210>; contentOffset: {0, 0}>

Ce qui montre clairement que la trame change entre les deux appels. Je voulais faire la configuration avec la vue dans la viewDidLoadméthode, mais si le contenu n'est pas disponible pour moi de changer jusqu'à ce qu'il soit à l'écran, cela semble assez inutile. Que pourrait-il se passer?

Jumhyn
la source
2
Utilisez-vous la mise en page automatique? ajoutez-vous cette vue dans le générateur d'interface ou par programme?
Andrea
La mise en page automatique est activée et cette vue est créée dans IB à partir d'un storyboard.
Jumhyn le
1
Je n'ai jamais utilisé de storyboard, mais il est plus probable qu'il soit correct. L'utilisation du cadre de mise en page automatique de vos vues est définie lorsque le moteur de mise en page automatique commence son calcul. Essayez de demander la même chose juste après la méthode super de - (void) viewDidLayoutSubviews de votre contrôleur de vue.
Andrea
Cela déclenche avec succès mon événement au bon moment, mais cette méthode est également appelée chaque fois que j'effectue une animation sur la vue.
Jumhyn
1
viewDidLayoutSubviewsétait la bonne voie à suivre. Je devais juste mettre tout mon contenu dans une sous-vue pour que la méthode ne soit pas rappelée chaque fois que je changeais le cadre de la vue principale.
Jumhyn

Réponses:

111

Autolayoutfait un énorme changement dans la façon dont nous concevons et développons l'interface graphique de nos vues. L'une des principales différences est que autolayoutcela ne change pas la taille de nos vues immédiatement, mais seulement quand est déclenché, c'est-à-dire à un moment précis, mais nous pouvons le forcer à recalculer immédiatement nos contraintes ou à les marquer comme "nécessitant" une mise en page. Cela fonctionne comme -setNeedDisplay.
Le grand défi pour moi était de comprendre et d'accepter cela, nous n'avons plus besoin d'utiliser des masques de redimensionnement automatique, et le cadre est devenu une propriété inutile pour placer nos vues. Nous n'avons plus besoin de penser à la position des vues, mais nous devons réfléchir à la manière dont nous voulons les voir dans un espace lié les uns aux autres.
Lorsque nous voulons mélanger l'ancien masque de redimensionnement automatique et la mise en page automatique, c'est lorsque des problèmes surviennent. Nous devrions penser à l'implémentation de la disposition automatique très bientôt et essayer d'éviter de mélanger l'ancienne approche dans une hiérarchie de vues basée sur la disposition automatique.
C'est bien d'avoir une vue de conteneur qui n'utilise que des masques de redimensionnement automatique, comme une vue principale d'un contrôleur de vue, mais c'est mieux si nous n'essayons pas de mélanger.
Je n'ai jamais utilisé de storyboard, mais il est plus probable qu'il soit correct. Grâce à la mise en page automatique, le cadre de vos vues est défini lorsque le moteur de mise en page automatique commence son calcul. Essayez de demander la même chose juste après la super - (void)viewDidLayoutSubviewsméthode de votre contrôleur de vue.
Cette méthode est appelée lorsque le moteur de mise en page automatique a terminé de calculer les cadres de vos vues.

Andrea
la source
5
- (void) viewDidLayoutSubviews est la réponse pour moi! Merci beaucoup!
FrizzTheSnail
Cette réponse n'est vraiment pas correcte. Oui bien sûr, évidemment, depuis (5?) Ans, vous devez utiliser la mise en page automatique. Mais il existe un certain nombre de situations (en utilisant la mise en page automatique ) où vous devez, par exemple, ajouter quelque chose sur un écran, "juste avant qu'il n'apparaisse à l'utilisateur". (Si vous le faites dans viewDidAppear, vous obtiendrez un scintillement. Si vous le faites dans viewWillAppear - les positions seront fausses.) La réponse réelle est en effet d'utiliser viewDidLayoutSubviews.
Fattie
add something on a screen, "just before it appears to the user". The actual answer is indeed to use viewDidLayoutSubviews.... vous allez montrer cette vue plusieurs fois
Andrea
156

De la documentation:

viewWillAppear:

Avertit le contrôleur de vue que sa vue est sur le point d'être ajoutée à une hiérarchie de vues.

viewDidAppear:

Avertit le contrôleur de vue que sa vue a été ajoutée à une hiérarchie de vues.

En conséquence, les cadres des sous-vues ne sont pas encore définis dans viewWillAppear:

La méthode appropriée pour modifier votre interface utilisateur avant que la vue ne soit présentée à l'écran est:

viewDidLayoutSubviews

Avertit le contrôleur de vue que sa vue vient de présenter ses sous-vues.

gsach
la source
2
Ugh, c'est ennuyeux. Même le libellé de la documentation donne l'impression que viewWillApepar: et viewDidAppear: devraient se produire directement l'un après l'autre.
Jumhyn le
2 mois d'attente pour cette réponse ... merci je l'ai trouvée !! MERCI BEAUCOUP!
Rafael Ruiz Muñoz
6
Il convient de noter qu'il viewDidLayoutSubviewssera appelé plusieurs fois et pas toujours avec la même trame, (je pense que cela s'appelle parfois avec CGRectZero au premier appel). Il est appelé pour chaque sous-vue ajoutée et autres modifications de la vue.
bauerMusic
Je me suis penché sur cela toute la journée (viewDidLayoutSubviews étant appelé plusieurs fois, y compris lors du rejet de la vue), et je viens de dire le ef avec, et de mettre la logique dans une instruction if et de créer un bool qui devient vrai après le le code a été exécuté une fois. Je pense qu'il doit y avoir un moyen plus propre, mais il y a déjà trop de temps.
solénoïde
nous devons aller au fond des choses! viewDidLayoutSubviews est horrible, seNeedDisplay ne fonctionne pas
Yaro
9

appel

self.scrollView.layoutIfNeeded ()

dans votre viewWillAppearméthode. Ensuite, vous pouvez accéder à son cadre et il aura la même valeur que vous imprimez dansviewDidAppear

Andrei
la source
1
Non, ce n'est pas correct. Exemple: vous avez une barre de navigation à l'écran (depuis votre contrôleur de navigation). Même après layoutIfNeeded (), la hauteur de la barre de navigation n'est pas incluse, donc la taille de votre cadre changera.
xaphod
C'EST un bon moyen de forcer un recalcul d'une dimension de cadre scrollview afin qu'il soit cohérente dans la mise en page de vue hiérarchique des appels si le cadre ScrollView est lié à des contraintes dans son superview.
smakus
4

Dans mon cas, déplacer toutes les méthodes liées au cadre vers

override func viewWillLayoutSubviews()

fonctionnait parfaitement (j'essayais de modifier les contraintes du storyboard).

Siddhesh Mahadeshwar
la source