web-dev-qa-db-fra.com

Obtenir les limites correctes de la vue de UIViewController

J'ai une application iPad. En orientation paysage, la vue réelle de UIViewController = 1024px et hauteur = 768 - 20 (statusBar) - 44 (navigationBar) = 704px.

Je veux donc obtenir ce [1024 x 704] taille et j'utilise self.view.bounds pour ça. Il renvoie [748 x 1024], ce qui est faux! Mais lorsque je fais pivoter l'écran deux fois (courant -> portrait -> courant), les limites de la vue sont correctes - [1024 x 704].

La vue a été initialisée comme ceci:

- (void)loadView {
    self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
    self.view.backgroundColor = [UIColor lightGrayColor];
}

Et les limites étaient comme ceci:

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"bounds = %@", NSStringFromCGRect(self.view.bounds));
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    NSLog(@"bounds = %@", NSStringFromCGRect(self.view.bounds));
}

La question est donc ... Comment puis-je obtenir la vue correcte liée au tout début?

41
demon9733

Selon certaines des autres réponses, le problème que vous voyez est dû au fait que viewDidLoad est appelé avant la rotation se produit. Étant donné que l'iPad s'initialise toujours en mode portrait, si vous obtenez les valeurs de taille dans viewDidLoad, ce seront toujours les tailles de portrait - ceci indépendamment des orientations que vous avez configurées.

Pour obtenir la taille après l'orientation/rotation se produit, obtenez les valeurs de taille dans viewDidAppear.


Je ne comprends pas particulièrement pourquoi iOS ne gère pas cela mieux - d'autant plus que vous définissez les orientations dans les paramètres du projet et dans Xcode Interface Builder. Mais je suis sûr qu'il y a une bonne raison ;-).

33
RichS

Comment faire cela correctement

Votre sous-classe UIViewController doit remplacer la méthode viewWillLayoutSubviews, voir aussi ici .

Lorsque cette méthode est appelée, le view du viewController a sa taille correcte et vous pouvez apporter les ajustements nécessaires aux sous-vues avant le passage de la disposition sur les sous-vues.

Swift

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    NSLog("bounds = \(self.view.bounds)")
}

Obj-C

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    NSLog(@"bounds = %@", NSStringFromCGRect(self.view.bounds));
}

Documentation

Lorsque les limites d'une vue changent , la vue ajuste la position de ses sous-vues. Votre contrôleur de vue peut remplacer cette méthode pour apporter des modifications avant que la vue ne présente ses sous-vues. L'implémentation par défaut de cette méthode ne fait rien.

Comme vous le voyez dans la partie soulignée, cette méthode est appelée à chaque fois la vue du contrôleur de vue change de taille, ainsi que lorsque la vue apparaît pour la première fois. Cela vous permet de répondre correctement à la rotation et aux autres événements de changement de limites

Plusieurs façons qui ne fonctionnent pas

Quelques approches suggérées dans d'autres réponses ne fonctionnent pas bien ou présentent de graves inconvénients. Malheureusement, cela inclut la réponse sélectionnée. Je vous exhorte à éviter ces approches. Je vais les parcourir et discuter des raisons pour lesquelles vous ne devriez pas les utiliser. S'il vous plait, ne le faites pas.

  • viewDidLoad et viewWillAppear - lors de ces appels view n'a pas encore sa taille finale. La taille que vous obtenez ne sera jamais correcte que par pur hasard , afin de vous induire en erreur.
  • viewDidAppear - c'est trop tard. Votre vue est déjà à l'écran et visible par l'utilisateur . Faire des changements ici entraînera des changements visibles/des problèmes brusques et semblera amateur. Encore une fois, s'il vous plaît - pour vous, pour moi, pour tout le monde pour l'amour: ne le faites pas! Vous êtes meilleur que cela, tout comme vos utilisateurs.
  • UIScreen.mainScreen.bounds.size - c'est extrêmement bas niveau. Vous implémentez un UIViewController et la taille de sa vue dépend des contrôleurs dans lesquels il est imbriqué (navigation, onglet, pagination, tous les contrôleurs personnalisés, etc.), de la façon dont l'appareil est tourné et, potentiellement, de la façon dont le l'écran a été divisé pour le multitâche. Donc, alors que vous pourriez-vous être en mesure de compenser tout cela et de calculer la taille finale de votre vue, vous vous retrouverez avec un code complexe et fragile qui peut facilement se casser si Apple décide de modifier l'une de ces mesures. UIViewController fera tout cela pour vous si vous remplacez simplement viewWillLayoutSubviews.

En plus de ne pas fournir des informations correctes, ces approches problématiques ne vous aideront pas avec la rotation automatique ou d'autres événements qui font changer la taille de la vue du contrôleur de vue, comme les gestes multitâches. C'est quelque chose que vous voulez vraiment gérer en douceur.

Alors s'il vous plaît: soyez un champion. Faites-le de la bonne façon. Utilisez viewWillLayoutSubviews. Votre implémentation sera appelée pour chaque changement de taille, et vos utilisateurs, votre futur moi, les membres de l'équipe et moi vous célébrerons pour cela. Bravo!

Autres conseils

Lorsque viewWillLayoutSubviews est appelé, la seule vue dans votre hiérarchie qui sera redimensionnée à sa taille finale est viewController.view. Le donner pour cela est au nom de la méthode. Ça vous dit view… (racine de votre contrôleur de vue view) …WillLayout… (très bientôt maintenant, mais ce n'est pas arrivé pour l'instant) …Subviews (tout le reste dans sa hiérarchie sous la vue racine).

La mise en page des sous-vues ne s'est donc pas encore produite. Chaque enfant sous la racine ne le fait pas a encore une taille finale valide. Toutes les informations de taille que vous interrogez dans les vues enfants seront au mieux complètement fausses.

Plus probablement, et énormément pire , il sera trompeusement correct .

Il arrive pour être ce que vous attendez et ce dont vous avez besoin en raison d'un défaut à cette taille et orientation, ou en raison de vos paramètres de storyboard. Mais ce n'est que par hasard et ce n'est pas quelque chose sur lequel vous pouvez compter avec différentes tailles ou orientations d'appareils.

Si vous devez être averti lorsqu'une sous-vue particulière change de taille et connaître sa taille finale exacte, vous devez généralement remplacer la méthode layoutSubviews de cette sous-classe UIView particulière.

27
Benjohn

J'ai toujours utilisé:

CGSize sizeOfScreen = [[UIScreen mainScreen] bounds].size;

pour obtenir la taille de l'écran, et:

CGSize sizeOfView = self.view.bounds.size;

pour obtenir la taille de view. Je viens de le tester sur viewDidLoad et il a renvoyé:

2012-07-17 10:25:46.562 Project[3904:15203] bounds = {768, 1004}

Ce qui est correct puisque le CGSize est défini comme {Largeur, Hauteur}.

13
luksfarris

C'est ce que j'ai trouvé dans mon dernier projet: le cadre de self.view sera ajusté après viewDidLoad selon que cet écran a une barre de navigation etc.

Donc, vous pouvez peut-être utiliser cette valeur après viewDidLoad (peut-être dans viewWillAppear ou viewDidAppear) ou l'ajuster manuellement en soustrayant la hauteur des barres.

2
Selkie

J'ai rencontré ce problème et j'ai constaté que l'obtention des limites dans viewDidAppear fonctionnait pour mes besoins.

2
Dustin Kendall