web-dev-qa-db-fra.com

UIViewController renvoie une trame invalide?

Lorsque je démarre mon ViewController en mode landscape (après l'appel de viewDidLoad), j'imprime le cadre, il me donne la taille du cadre pour le mode portrait à la place.

Est-ce un bug des suggestions?

- (void)viewDidLoad
{
   [super viewDidLoad];

   NSLog(@"%@", NSStringFromCGRect(self.view.frame));
   // Result is (0, 0 , 768, 1024)
}
38
aryaxt

Il y a quelques choses que vous ne comprenez pas.

Tout d'abord, le système vous envoie viewDidLoad immédiatement après le chargement de votre nib. Il n'a même pas encore ajouté la vue à la hiérarchie des vues. Il n'a donc pas non plus redimensionné votre vue en fonction de la rotation de l'appareil.

Deuxièmement, le cadre d'une vue se trouve dans l'espace de coordonnées de sa vue supervisée. S'il s'agit de votre vue racine, sa vue supervisée sera la UIWindow (une fois que le système ajoute réellement votre vue à la hiérarchie des vues). Le UIWindow gère la rotation en définissant la transformation de sa sous-vue. Cela signifie que le cadre de la vue ne sera pas nécessairement celui que vous attendez.

Voici la hiérarchie des vues en orientation portrait:

(lldb) po [[UIApp keyWindow] recursiveDescription]
(id) $1 = 0x09532dc0 <UIWindow: 0x9632900; frame = (0 0; 768 1024); layer = <UIWindowLayer: 0x96329f0>>
   | <UIView: 0x9634ee0; frame = (0 20; 768 1004); autoresize = W+H; layer = <CALayer: 0x9633b50>>

et voici la hiérarchie des vues en orientation paysage gauche:

(lldb) po [[UIApp keyWindow] recursiveDescription]
(id) $2 = 0x09635e70 <UIWindow: 0x9632900; frame = (0 0; 768 1024); layer = <UIWindowLayer: 0x96329f0>>
   | <UIView: 0x9634ee0; frame = (20 0; 748 1024); transform = [0, -1, 1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0x9633b50>>

Notez qu'en orientation paysage, la taille du cadre est de 748 x 1024, non 1024 x 748.

Ce que vous voulez probablement regarder, s'il s'agit de votre vue racine, ce sont les limites de la vue:

(lldb) p (CGRect)[0x9634ee0 bounds]
(CGRect) $3 = {
  (CGPoint) Origin = {
    (CGFloat) x = 0
    (CGFloat) y = 0
  }
  (CGSize) size = {
    (CGFloat) width = 1024
    (CGFloat) height = 748
  }
}

Vous voulez probablement savoir quand la transformation, le cadre et les limites de la vue sont mis à jour. Si l'interface est en orientation paysage lorsque votre contrôleur de vue charge sa vue, vous recevrez les messages dans cet ordre:

{{0, 0}, {768, 1004}} viewDidLoad
{{0, 0}, {768, 1004}} shouldAutorotateToInterfaceOrientation:
{{0, 0}, {768, 1004}} shouldAutorotateToInterfaceOrientation:
{{0, 0}, {768, 1004}} viewWillAppear:
{{0, 0}, {768, 1004}} shouldAutorotateToInterfaceOrientation:
{{0, 0}, {768, 1004}} shouldAutorotateToInterfaceOrientation:
{{0, 0}, {768, 1004}} willRotateToInterfaceOrientation:duration:
{{0, 0}, {1024, 748}} viewWillLayoutSubviews
{{0, 0}, {1024, 748}} layoutSubviews
{{0, 0}, {1024, 748}} viewDidLayoutSubviews
{{0, 0}, {1024, 748}} willAnimateRotationToInterfaceOrientation:duration:
{{0, 0}, {1024, 748}} shouldAutorotateToInterfaceOrientation:
{{0, 0}, {1024, 748}} viewDidAppear:

Vous pouvez voir que les limites de votre vue changent après vous recevez willRotateToInterfaceOrientation:duration: et avant vous recevez viewWillLayoutSubviews.

Les méthodes viewWillLayoutSubviews et viewDidLayoutSubviews sont nouvelles dans iOS 5.0.

Le message layoutSubviews est envoyé à la vue, pas au contrôleur de vue, vous devrez donc créer une sous-classe UIView personnalisée si vous souhaitez l'utiliser.

158
rob mayoff

Voici une solution propre.

Je rencontrais le même problème et j'insiste pour utiliser frame dans mon application. Je ne veux pas faire d'exception pour le contrôleur de vue racine. J'ai remarqué que frame du contrôleur de vue racine affichait des dimensions de portrait, mais toutes les sous-vues avaient des dimensions de paysage correctes.

Étant donné que seul le contrôleur de vue racine se comporte différemment, nous pouvons définir un contrôleur de vue vide standard en tant que contrôleur de vue racine et y ajouter notre contrôleur de vue personnalisé. (Code ci-dessous.)

Vous pouvez ensuite utiliser frame comme vous le souhaitez, dans votre contrôleur de vue personnalisé.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.

    self.window.rootViewController = [[UIViewController alloc] init];

    [self.window makeKeyAndVisible];

    self.viewController = [[ViewController alloc] initWithNibName:nil bundle:nil];
    [self.window.rootViewController addChildViewController:self.viewController];
    [self.window.rootViewController.view addSubview:self.viewController.view];

    return YES;
}
3
Timo

Voici une solution alternative (Swift 4):

override func viewDidLoad() {
    super.viewDidLoad()
    self.view.setNeedsLayout()
    self.view.layoutIfNeeded()
}

Fondamentalement, cela force le contrôleur de vue à disposer correctement sa vue et à partir de là, vous pouvez obtenir les images correctes pour toutes vos sous-vues. Cela aide particulièrement lorsque vous effectuez des animations de transition et que vos contrôleurs de vue utilisent la mise en page automatique et le générateur d'interface.

D'après ce que j'ai remarqué, il semble que les cadres initiaux sont définis sur la classe de taille par défaut de votre constructeur d'interface. J'édite normalement en utilisant la classe de taille de l'iPhone XS, donc dans viewDidLoad, il semble que la largeur de la vue soit toujours de 375, que vous utilisiez ou non un iPhone XR. Cela se corrige avant viewWillAppear cependant.

Le code ci-dessus corrigera ce problème et vous permettra d'obtenir les images correctes pour votre vue/sous-vues avant que le contrôleur de vue ne soit rendu à l'écran.

0
Nick Kirsten