web-dev-qa-db-fra.com

Le moyen le plus simple de prendre en charge plusieurs orientations? Comment charger une NIB personnalisée lorsque l'application est en mode paysage?

J'ai une application dans laquelle je souhaite soutenir plusieurs orientations. J'ai deux fichiers .xib que je veux utiliser, myViewController.xib et myViewControllerLandscape.xib. myViewController.xib existe dans le projet/Ressources et myViewControllerLandscape.xib existe dans le répertoire racine du projet.

Ce que je veux faire, c'est utiliser une NIB séparée (myViewControllerLandscape.xib) pour mes rotations. J'essaie de détecter la rotation dans viewDidLoad lcomme ceci:

if((self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight))
 {
  NSLog(@"Landscape detected!");
  [self initWithNibName:@"myViewControllerLandscape" bundle:nil];

 }

Mais je peux voir dans gdb que cela n'est pas exécuté lorsque l'application est démarrée avec l'appareil en mode paysage. Le message NSLog ne se déclenche pas. Pourquoi est-ce? Qu'est ce que j'ai mal fait?

De plus, si je mets explicitement l'appel de fonction initWithNibName dans la méthode viewDidLoad, cette nib n'est pas chargée et continue avec le fichier myViewController.xib. Quel est le problème avec mon appel? Dois-je spécifier un forfait?

Merci!

35
Peter Hajas

Vous devez charger une vue, puis vérifier l'orientation et en charger une autre si nécessaire. Vous vérifiez l'orientation dans shouldAutorotateToInterfaceOrientation: renvoyant oui si vous voulez faire pivoter.

J'utilise un contrôleur de navigation pour gérer la transition. Si la vue portrait est relevée et que l'appareil tourne, j'appuie sur la vue paysage, puis j'éclate la vue paysage lorsqu'elle revient en mode portrait.

Éditer:

Je renvoie OUI pour toutes les orientations dans shouldAutorotateToInterfaceOrientation: mais cela sera-t-il appelé au lancement de l'application? Poussez-vous votre vue à l'intérieur de cette fonction?

Les constantes d'orientation ne sont pas des globales que vous interrogez mais plutôt une partie des messages envoyés au contrôleur par le système. En tant que tel, vous ne pouvez pas facilement détecter l'orientation avant le chargement d'un contrôleur de vue. Au lieu de cela, vous câblez l'application pour démarrer dans une orientation particulière (généralement portrait), puis la faire pivoter immédiatement. (Voir Safari mobile. Il commence toujours en mode portrait puis tourne en mode paysage.)

Ce sont les deux méthodes que j'ai utilisées pour échanger mes vues portrait et paysage.

Les trois contrôleurs de vue ont cette méthode:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

Le portrait a ceci:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {

    if (toInterfaceOrientation==UIInterfaceOrientationLandscapeRight) {
        [self.nav pushViewController:rightLVC animated:NO];
    }
    if (toInterfaceOrientation==UIInterfaceOrientationLandscapeLeft) {
        [self.nav pushViewController:leftLVC animated:NO];
    }
}

Chaque contrôleur de paysage a ceci:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {

    if (toInterfaceOrientation==UIInterfaceOrientationPortrait) {
        [self.nav popViewControllerAnimated:NO];
    }

L'application démarre en mode portrait. Si l'orientation de l'appareil est paysage, cela pousse les paysages appropriés. Lorsque l'appareil repasse en mode portrait, il apparaît dans le paysage. Pour l'utilisateur, il ressemble à la même vue se réorganisant pour une orientation différente.

40
TechZen

J'ai trouvé un bien meilleur moyen qui fonctionne indépendamment du contrôleur de navigation. Pour le moment, cela fonctionne quand il est intégré dans un contrôleur de navigation et lorsqu'il n'est PAS intégré (bien que je n'utilise pas le contrôleur de navigation pour le moment, il peut donc y avoir un bug que je n'ai pas vu - par exemple, l'animation de transition Push pourrait disparaître drôle, ou quelque chose)

Deux NIB, utilisant la convention de dénomination d'Apple. Je soupçonne que dans iOS 6 ou 7, Apple pourrait l'ajouter en tant que "fonctionnalité". Je l'utilise dans mes applications et cela fonctionne parfaitement:

  1. les déclencheurs sur tourneront, ne DEVRAIENT PAS tourner (attend que l'animation de rotation soit sur le point de commencer)
  2. utilise la Apple pour les fichiers paysage/portrait (Default.png est Default-landscape.png si vous voulez Apple pour charger automatiquement une version paysage))
  3. recharge la nouvelle NIB
  4. qui réinitialise le self.view - cela mettra à jour automatiquement l'affichage
  5. puis il appelle viewDidLoad (Apple ne l'appellera PAS pour vous, si vous rechargez manuellement une NIB)
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    if( UIInterfaceOrientationIsLandscape(toInterfaceOrientation) )
    {
        [[NSBundle mainBundle] loadNibNamed: [NSString stringWithFormat:@"%@-landscape", NSStringFromClass([self class])]
                                      owner: self
                                    options: nil];
        [self viewDidLoad];
    }
    else
    {
        [[NSBundle mainBundle] loadNibNamed: [NSString stringWithFormat:@"%@", NSStringFromClass([self class])]
                                      owner: self
                                    options: nil];
        [self viewDidLoad];
    }
}
59
Adam

Vraiment comme la réponse d'Adam - je l'ai légèrement modifiée pour permettre le chargement initial de la plume en mode portrait ou paysage

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    if( !nibNameOrNil )     nibNameOrNil = [self nibNameRotated:[[UIApplication sharedApplication] statusBarOrientation]];
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    return self;
}

- (NSString*) nibNameRotated:(UIInterfaceOrientation)orientation
{
    if( UIInterfaceOrientationIsLandscape(orientation))     return [NSString stringWithFormat:@"%@-landscape", NSStringFromClass([self class])];
    return [NSString stringWithFormat:@"%@", NSStringFromClass([self class])];
}

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    NSString *nibname = [self nibNameRotated:toInterfaceOrientation];
    [[NSBundle mainBundle] loadNibNamed:nibname owner:self options:nil];
    [self viewDidLoad];
}
5
amergin

J'aime la réponse acceptée de TechZen mais comme indiqué dans le commentaire d'Adam, il laisse le contrôleur de vue portrait dans la pile de navigation lorsque vous tournez en paysage. Cela signifie que toucher le bouton de retour dans la barre de navigation en mode paysage amènera l'utilisateur à la vue portrait. Un collègue et moi essayons l'approche de supprimer manuellement l'élément supplémentaire de la pile de navigation. Cela semble fonctionner mais je n'ai encore que très peu d'expérience, je ne fais donc aucune promesse. Voici un exemple de code:

Pour passer du portrait au paysage:

[[self navigationController] pushViewController:landscapeViewController animated:NO];
[self removeFromNavigationStack:[MyPortraitViewController class]];

Pour revenir au portrait:

[[self navigationController] pushViewController:portraitViewController animated:NO];
[self removeFromNavigationStack:[MyLandscapeViewController class]];

Si vous n'utilisez pas d'animation: NON, vous pouvez recevoir des avertissements concernant l'état de la navigation.

Assistant:

- (void)removeFromNavigationStack:(Class)oldClass {
      UINavigationController *nav = [self navigationController];
      NSMutableArray *newStack = [NSMutableArray array];
      for (UIViewController *vc in nav.viewControllers) {
          if (![vc isMemberOfClass:[oldClass class]]) {
              [newStack addObject: vc];            
          }
      [nav setViewControllers:newStack animated:NO];
  }
1
Deanna Gelbart

Un autre choix, TPMultiLayoutViewController .

1
ohho