web-dev-qa-db-fra.com

Vue sur tout: sous-vue UIWindow VS sous-vue UIViewController

Afin de créer un UIView au-dessus de toutes les vues comme le comportement de UIAlertView, la meilleure façon que je vois est de le mettre dans la fenêtre de l'application, ce qui consiste à ajouter la vue à:

[[[UIApplication sharedApplication] delegate] window]

Cependant, l'inconvénient que j'ai trouvé est qu'il ne prend pas automatiquement en rotation. Pour qu'il prenne en rotation, la meilleure façon de faire est d'ajouter la vue sur AppDelegate actuel window.navigationController/rootViewController, cependant, ce faisant, il ne sera plus au-dessus de tout. Disons que lorsque la vue s'affiche et qu'il y a une fenêtre contextuelle modalViewController, la vue modale couvrira certainement la vue.

Ma question est, est-il possible d'ajouter une sous-vue à la fenêtre la plus haute et supporte l'orientation? Ai-je besoin de deux jeux de fichiers Nib dans ce cas? Comment UIAlertView fait la magie pour combiner le support supérieur et d'orientation? Quand je regarde dans le UIAlertView.h, Je vois qu'il y a en fait 2 UIWindows, une fois s'appelle originalWindow et une autre appelée dimWindow. Y a-t-il un endroit où je peux trouver le code source de UIAlertView.m?

27
Zian Chen

La meilleure solution que j'ai trouvée consiste à créer une vue de conteneur transparente, à ajouter ce conteneur à la fenêtre et à placer votre alerte à l'intérieur du conteneur. Vous pouvez ensuite vous inscrire à UIApplicationWillChangeStatusBarOrientationNotification pour recevoir des événements de rotation et transformer le conteneur; cela vous permet de manipuler indépendamment le cadre de l'alerte:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{
     ...
     self.container = [[UIView alloc] initWithFrame:self.window.bounds];
     [self.container addSubview:self.alertView];
     [self.window addSubview:self.container];
     [[NSNotificationCenter defaultCenter] addObserver:self 
                                              selector:@selector(statusBarWillRotate:) 
                                                  name:UIApplicationWillChangeStatusBarOrientationNotification 
                                                object:nil];
     ...
}
...
- (void)statusBarWillRotate:(NSNotification *)theNotification
{
    CGFloat rotation = 0;
    switch ([notification[UIApplicationStatusBarOrientationUserInfoKey] intValue])
    {
        case UIInterfaceOrientationLandscapeLeft:
            rotation = -M_PI_2;
            break;
        case UIInterfaceOrientationLandscapeRight:
            rotation = M_PI_2;
            break;
        case UIInterfaceOrientationPortraitUpsideDown:
            rotation = M_PI;
            break;
        case UIInterfaceOrientationPortrait: 
        default:
            rotation = 0;
            break;
    }
    CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(rotation);
    CGSize rotatedSize = CGRectApplyAffineTransform(self.window.bounds, rotationTransform).size;
    [UIView animationWithDuration:[UIApplication sharedApplication].statusBarOrientationAnimationDuration
                       animations:^{
         self.container.transform = rotationTransform;
         // Transform invalidates the frame, so use bounds/center
         self.container.bounds = CGRectMake(0, 0, rotatedSize.width, rotatedSize.height);
         self.container.center = CGPointMake(self.window.bounds.size.width / 2, self.window.bounds.size.height / 2);
     }];
}

Vous pouvez, si vous le souhaitez, créer une fenêtre entièrement nouvelle avec sa propriété windowLevel définie sur UIWindowLevelAlert, ce qui garantira que la fenêtre restera au-dessus de toutes les autres vues, y compris le clavier, mais la gestion des fenêtres devient un peu délicat. La solution ci-dessus devrait suffire pour la plupart des besoins.

Le problème avec simplement l'ajout d'une vue d'un contrôleur de vue à une fenêtre est qu'il n'est pas garanti de recevoir toute la rotation et view[Will|Did][Disa|A]ppear: messages sauf s'il est ajouté au contrôleur de vue hierarchy via addChildViewController: sur le contrôleur de vue racine.

15
Austin

Seule la première sous-vue de UIWindow est informée des changements d'orientation. (<iOS8)

Votre question est très similaire à celle-ci:

Plusieurs vues dans une fenêtre UIWindow

Comme le dit la réponse, vous pouvez vous inscrire aux notifications de changements d'orientation et gérer le relais de votre sous-vue `` en haut '' de cette façon.

4
occulus

Voici une solution possible, j'explique en quelques étapes.

  1. Commencer avec une classe héritée UIViewController au lieu de UIView, Disons que la classe nommée TopViewController héritée par UIViewController
  2. Dans la mesure où vous ne pouvez pas enregistrer la rotation de périphérique/interface dans la classe héritée UIView, la classe TopViewController héritée de UIViewController possède des méthodes répondant à Afficher les événements de rotation. Les événements de rotation peuvent être capturés maintenant.
  3. Enfin, vous pouvez placer la vue TopViewController en haut de UIWindow en utilisant la même méthode que celle que vous avez mentionnée dans votre question.

    [[[[UIApplication sharedApplication] delegate] window] addSubview:(TopViewController object).view];
    

J'espère que cela t'aides.

2
Rahul