100 votes

iOS 6 : Comment limiter certaines vues au portrait et autoriser d'autres à pivoter ?

J'ai une application iPhone qui utilise un UINavigationController pour présenter une interface d'exploration : D'abord une vue, puis une autre, jusqu'à quatre niveaux de profondeur. Je veux que les trois premières vues soient limitées à l'orientation portrait et que seule la dernière vue soit autorisée à pivoter vers le paysage. Lorsque l'on passe de la quatrième vue à la troisième et que la quatrième vue était en orientation paysage, je veux que tout revienne en orientation portrait.

Dans iOS 5, j'ai simplement défini shouldAutorotateToInterfaceOrientation: dans chacun de mes contrôleurs de vue pour renvoyer des OUI pour les orientations autorisées. Tout a fonctionné comme décrit ci-dessus, y compris le retour au format portrait même si l'appareil était tenu en orientation paysage lors du retour du contrôleur de vue n° 4 au n° 3.

Dans iOS 6, tous les contrôleurs de vue pivotent en mode paysage, ce qui casse ceux qui n'étaient pas censés le faire. Les notes de mise à jour d'iOS 6 indiquent

L'application et le délégué à l'application assument de plus en plus de responsabilités. Maintenant, les conteneurs iOS (tels que UINavigationController ) ne consultent pas leurs enfants pour déterminer s'ils doivent faire une autorotation. [...] Le système demande au contrôleur de vue plein écran le plus haut placé (généralement le contrôleur de vue racine) ses orientations d'interface prises en charge chaque fois que le dispositif pivote ou qu'un contrôleur de vue est présenté avec le style de présentation modal plein écran. En outre, les orientations prises en charge ne sont récupérées que si ce contrôleur de vues renvoie un OUI de son paramètre shouldAutorotate méthode. [...] Le système détermine si une orientation est prise en charge en croisant la valeur renvoyée par la méthode de l'application supportedInterfaceOrientationsForWindow: avec la valeur renvoyée par la méthode supportedInterfaceOrientations du contrôleur plein écran le plus élevé.

J'ai donc sous-classé UINavigationController J'ai donné mon MainNavigationController une propriété booléenne landscapeOK et l'a utilisé pour renvoyer les orientations autorisées en supportedInterfaceOrientations . Ensuite, dans chacun de mes contrôleurs de vue viewWillAppear: méthodes J'ai une ligne comme ceci

    [(MainNavigationController*)[self navigationController] setLandscapeOK:YES];

pour dire à mon MainNavigationController le comportement souhaité.

Voici la question : Si je me rends maintenant à ma quatrième vue en mode portrait et que je retourne le téléphone, il pivote en mode paysage. J'appuie alors sur le bouton Retour pour revenir à la troisième vue, qui est censée fonctionner en mode portrait uniquement. Mais il ne pivote pas en arrière. Comment faire pour qu'il le fasse ?

J'ai essayé

    [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait]

dans le viewWillAppear de mon troisième contrôleur de vue, mais cela ne fait rien. S'agit-il de la mauvaise méthode à appeler ou peut-être du mauvais endroit pour l'appeler ou devrais-je mettre en œuvre l'ensemble de la chose d'une manière totalement différente ?

96voto

Flo Points 1005

J'ai eu le même problème et j'ai trouvé une solution qui fonctionne pour moi. Pour que cela fonctionne, il faut pas suffisante pour mettre en œuvre - (NSUInteger)supportedInterfaceOrientations dans votre UINavigationController. Vous devez également implémenter cette méthode dans votre contrôleur n°3, qui est le premier à être en mode portrait après le contrôleur n°4. J'ai donc le code suivant dans mon UINavigationController :

- (BOOL)shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
    if (self.isLandscapeOK) {
        // for iPhone, you could also return UIInterfaceOrientationMaskAllButUpsideDown
        return UIInterfaceOrientationMaskAll;
    }
    return UIInterfaceOrientationMaskPortrait;
}

Dans le contrôleur de vue n°3, ajoutez ce qui suit :

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

Vous n'avez pas besoin d'ajouter quoi que ce soit à vos contrôleurs de vue #1, #2, et #4. Cela fonctionne pour moi, j'espère que cela vous aidera.

8 votes

Vous avez résolu quelque chose que les gens essayaient de résoudre depuis des mois ! vous méritez beaucoup de crédit pour cela ! J'ai ajouté supportedInterfaceOrientations à une catégorie sur UIViewController et cela fonctionne parfaitement comme un défaut pour ces contrôleurs en mode portrait.

3 votes

Parmi 100 réponses différentes sur SO, celle-ci est celle qui fonctionne vraiment. Merci :-)

0 votes

@dwery est-ce que c'est seulement iOS 6 ?

69voto

Kenpachi Points 1578

Añade un CustomNavigationController

Remplacez ces méthodes :

-(BOOL)shouldAutorotate
{
    return [[self.viewControllers lastObject] shouldAutorotate];
}

-(NSUInteger)supportedInterfaceOrientations
{
    return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}

Maintenant, ajoutez toutes les orientations dans le plist

enter image description here

Dans le contrôleur de vue, n'ajoutez que les éléments nécessaires :

-(BOOL)shouldAutorotate
{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

ces méthodes remplacent les méthodes du contrôleur de navigation

5 votes

Vous avez vraiment bien répondu à cette question ! Merci beaucoup. Vous m'avez vraiment beaucoup aidé. Cela a été un gros problème pour moi pendant un certain temps. Excellent travail.

0 votes

Merci ! Cela m'a pris tellement de temps et d'énergie à comprendre !

0 votes

Vous êtes un sauveur de vies....Cela fonctionne aussi dans mon application. En fait, ce que je veux dans mon application, c'est que j'aie toutes les données de l'application. ViewController sont en mode Portrait et ma seule ViewController est en mode Paysage et Portrait. J'apporte également mon soutien à la fois à iOS 5.0 et à iOS 6.0. C'est pourquoi je ne sais pas quoi faire, mais cette solution est excellente. CustomNavigationController dans mon contrôleur de vue Root et donner du support en fonction de mes besoins. Merci encore une fois.

9voto

Brian Points 1047

Après avoir examiné toutes les réponses à d'innombrables questions similaires sur SO, aucune des réponses ne m'a convenu, mais elles m'ont donné quelques idées. Voici comment j'ai fini par résoudre le problème :

Tout d'abord, assurez-vous que les Orientations de l'interface prises en charge dans la cible de votre projet contiennent toutes les orientations que vous souhaitez pour votre vue rotative.

enter image description here

Ensuite, faites une catégorie de UINavigationController (puisque Apple dit de ne pas le sous-classer) :

@implementation UINavigationController (iOS6AutorotationFix)

-(BOOL)shouldAutorotate {
    return [self.topViewController shouldAutorotate];
}

@end

Importez cette catégorie et le contrôleur de vue que vous voulez pouvoir faire pivoter (que j'appellerai RotatingViewController ) à votre contrôleur de vue de plus haut niveau, qui devrait contenir votre contrôleur de navigation. Dans ce contrôleur de vue, implémentez shouldAutorotate comme suit. Notez que ce ne doit pas être le même contrôleur de vue que celui que vous voulez faire pivoter.

-(BOOL)shouldAutorotate {

    BOOL shouldRotate = NO;

    if ([navigationController.topViewController isMemberOfClass:[RotatingViewController class]] ) {
        shouldRotate = [navigationController.topViewController shouldAutorotate];
    }

    return shouldRotate;
}

Enfin, dans votre RotatingViewController , mettre en œuvre shouldAutorotate y supportedInterfaceOrientations comme suit :

-(BOOL)shouldAutorotate {
    // Preparations to rotate view go here
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskAllButUpsideDown; // or however you want to rotate
}

La raison pour laquelle vous devez faire cela est que iOS 6 donne le contrôle de la rotation au contrôleur de la vue racine au lieu du contrôleur de la vue supérieure. Si vous voulez que la rotation d'une vue individuelle se comporte différemment des autres vues de la pile, vous devez écrire un cas spécifique pour cela dans le contrôleur de vue racine.

2 votes

C'est une excellente réponse qui mérite plus d'attention. Voici l'information qu'elle contient et que je n'ai pu trouver nulle part ailleurs, et qui est essentielle : Si vous voulez que la rotation d'une vue individuelle se comporte différemment de celle des autres vues de la pile, vous devez écrire un cas spécifique dans le contrôleur de la vue racine.

0 votes

Je suppose que cela fonctionnera si vous avez un navigationController comme premier membre du storyboard de votre application en tant que contrôleur initial, mais qu'en est-il du cas où le contrôleur initial est simplement un ViewController ?

4voto

micmdk Points 101

Je n'ai pas assez de réputation pour commenter la réponse de @Brian, alors je vais ajouter ma note ici.

Brian a mentionné qu'iOS6 donne le contrôle de la rotation au rootViewController - cela pourrait être non seulement un UINavigationController comme mentionné mais aussi un UITabBarController, ce qui a été le cas pour moi. Ma structure ressemble à ceci :

  • UITabBarController
    • UINavigationController
      • UIViewControllers ...
    • UINavigationController
      • UIViewControllers ...

J'ai donc ajouté les méthodes d'abord dans un UITabBarController personnalisé, puis dans un UINavigationController personnalisé et enfin dans le UIViewController spécifique.

Exemple à partir de UITabBarController et UINavigationController :

- (BOOL)shouldAutorotate {
    return [self.viewControllers.lastObject shouldAutorotate];
}

- (NSUInteger)supportedInterfaceOrientations {
    return [self.viewControllers.lastObject supportedInterfaceOrientations];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return [self.viewControllers.lastObject shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return [self.viewControllers.lastObject preferredInterfaceOrientationForPresentation];
}

0 votes

En fait, j'ai utilisé un UITabBarController comme variable d'instance dans mon contrôleur de vue racine parce que vous n'êtes pas censé sous-classer UITabBarController dans les versions d'iOS antérieures à 6.0 et que j'avais besoin de revenir à iOS 5.0.

0 votes

J'ai copié votre code et l'ai mis dans le contrôleur de navigation personnalisé et dans le contrôleur de barre d'outils personnalisé et j'ai défini d'autres contrôleurs de vue comme le contrôleur guidé de Brian, mais je n'ai toujours pas obtenu de contrôleur de vue fixe dans le portrait.

0 votes

@Brian j'ai le même problème. s'il vous plaît conseiller comment résoudre, j'ai copié votre code et mis dans le contrôleur de navigation personnalisé et customUabbarController et définir l'autre contrôleur de vue comme Micmdk's Guided. mais toujours pas obtenir le contrôleur de vue fixe en portrait dans ios8.

3voto

Harald Bögeholz Points 395

J'aimerais donner une réponse partielle à ma propre question. J'ai trouvé la ligne de code suivante, utilisée dans l'application viewWillAppear méthode de mon troisième UIViewController pour travailler :

[[UIDevice currentDevice] 
      performSelector:NSSelectorFromString(@"setOrientation:") 
           withObject:(id)UIInterfaceOrientationPortrait];

Mais je n'aime pas vraiment cette solution. Elle utilise une astuce pour assigner à une propriété en lecture seule qui, selon la documentation d'Apple, représente les physique l'orientation de l'appareil. C'est comme si l'on demandait à l'iPhone de passer à l'orientation correcte dans la main de l'utilisateur.

Je suis très tenté de laisser cela dans mon application car cela fonctionne tout simplement. Mais cela ne me semble pas correct et j'aimerais laisser la question ouverte pour une solution propre.

4 votes

Votre candidature sera très probablement rejetée si vous la laissez là. Je cherche également une solution à ce problème, mais il semble que ce ne soit pas possible. Tout ce que j'ai pu trouver est la réponse à cette question : stackoverflow.com/questions/7196300/ cela fonctionne en quelque sorte, mais pour une raison quelconque, la gestion des boutons tactiles dans l'un de mes viewcontrollers est interrompue.

0 votes

J'ai tenté ma chance et soumis à Apple. J'attends toujours la révision ... Je vous tiendrai au courant.

0 votes

Apple vient d'approuver mon application avec ce hack en place, yey ! J'espère que ça ne se retournera pas contre moi dans une future mise à jour d'iOS. En attendant, je suis toujours ouvert aux suggestions pour une solution propre !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X