Comment contraindre l'autorotation à une seule orientation pour certaines vues, tout en autorisant toutes les orientations sur d'autres?
Cette question concerne la rotation des périphériques iOS et les vues contrôlées multiples dans un UINavigationController. Certaines vues devraient être contraintes à l'orientation portrait, et certaines devraient autoroter librement . Si vous essayez de créer la configuration la plus simple avec trois vues, vous remarquerez que le comportement d'autorotation a quelques bizarreries très désagréables. Le scénario est, cependant, très simple, donc je pense que je ne fais pas l'implémentation d'autorotation correctement, ou j'oublie quelque.
J'ai une application de démonstration très basique qui montre l'étrangeté, et j'ai fait une vidéo le montrant en action.
- Télécharger l'application (projet XCode)
- voir les classes comme un essentiel (plutôt long)
- regardez la vidéo de la question (YouTube, 2m43s)
La configuration est très basique: trois contrôleurs de vue appelés FirstViewController
, SecondViewController
et {[2] } Tous étendent un {[3] } qui montre une étiquette avec le nom de la classe et qui renvoie YES for shouldAutorotateToInterfaceOrientation:
lorsque l'appareil est en mode portrait. SecondViewController remplace la méthode this pour permettre toutes les rotations. Les trois classes concrètes ajoutent quelques carrés colorés pour pouvoir naviguer entre les vues en poussant et en faisant éclater les contrôleurs sur / hors du UINavigationController
. Jusqu'à présent, un scénario très simple, je dirais.
Si vous maintenez l'appareil en orientation portrait ou paysage, c'est le résultat que je voudrais non seulement atteindre, mais je m'y attendais aussi. Dans la première image vous voyez que toutes les vues sont "droites", et dans la seconde vous voyez que seul le second contrôleur de vue contre-tourne l'orientation de l'appareil. Pour être clair, il devrait être possible de naviguer de la deuxième vue en mode paysage à la troisième, mais comme cette troisième ne prend en charge que l'orientation portrait, elle ne devrait être affichée qu'en orientation portrait. La façon la plus simple de voir si les résultats sont corrects est de regarder la position de la barre porteuse.
Mais ceci question est ici parce que le résultat réel est complètement différent. En fonction de la vue à laquelle vous vous trouvez lorsque vous faites pivoter le périphérique et de la vue vers laquelle vous naviguez ensuite, les vues ne tournent pas (pour être précis, la méthode didOrientFromInterfaceOrientation:
n'est jamais appelée). Si vous êtes en paysage sur le second et naviguez vers le troisième, il aura la même orientation que le second (=mauvais). Si vous naviguez de la seconde à la première cependant, l'écran tournera en mode portrait "forcé", et le carrier bar sera au sommet physique de l'appareil, quelle que soit la façon dont vous le tenez. La vidéo le montre plus en détail.
, Ma question est double:
- pourquoi le premier contrôleur de vue tourne-t-il en arrière, mais pas le troisième?
- Que Faut-il faire pour obtenir le bon comportement de vos vues lorsque vous voulez seulement que certaines vues soient autorotées, mais pas d'autres?
Santé, EP.
EDIT: en dernier recours avant de mettre un bounty à ce sujet, j'ai complètement réécrit cette question pour être plus courte, plus claire et, espérons-le, plus invitant à donner une réponse.
5 réponses
La réponse courte est que vous utilisez UINavigationController, et cela ne fonctionnera pas comme vous le souhaitez. À partir des documents D'Apple:
Pourquoi mon UIViewController ne tourne-t-il pas avec le périphérique?
Tous les contrôleurs de vue enfant dans votre UITabBarController ou UINavigationController ne sont pas d'accord sur un ensemble d'orientation commun.
Pour vous assurer que toutes les vues les contrôleurs tournent correctement, vous devez mettre shouldAutorotateToInterfaceOrientation pour chaque contrôleur de vue représentant chaque onglet ou niveau de navigation. Chacun doit convenir de la même orientation pour qui tournent pour se produire. C'est qu'ils tous devraient retourner Oui pour la même chose l'orientation des positions.
Vous pouvez en savoir plus sur voir les problèmes de rotation ici.
Vous devrez lancer votre propre gestion de la pile de vue / contrôleur pour ce que vous voulez faire.
Créez un délégué bolean in App pour contrôler l'orientation souhaitée, par exemple créez un bool pour activer le Portrait et dans votre contrôleur de vue, vous souhaitez autoriser L'activation du Portrait par une application partagée
Dans votre contrôleur de vue, où vous souhaitez activer ou désactiver l'orientation souhaitée.
((APPNAMEAppDelegate *)[[UIApplication sharedApplication] delegate]).enablePortrait= NO;
Délégué Dans L'Application.
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
NSLog(@"Interface orientations");
if(!enablePortrait)
return UIInterfaceOrientationMaskLandscape;
return UIInterfaceOrientationMaskLandscape|UIInterfaceOrientationMaskPortrait;
}
Ces méthodes seront déclenchées chaque fois que vous faites pivoter le périphérique, en fonction de ces BOOL activer l'orientation que vous voulez.
Il y avait une question similaire il y a quelques années avec un certain nombre de réponses. Voici une réponse récente de quelqu'un à cette question:
Existe-t-il un moyen documenté de définir l'orientation de l'iPhone?
D'après ce que je comprends, c'est un problème que beaucoup de gens ont et la plupart du temps les hacks sont le seul moyen de le résoudre. Regardez à travers ce fil si vous ne l'avez pas vu auparavant et voyez si quelque chose fonctionne pour vous.
Sur une note de côté, j'ai eu un problème similaire il y a un moment quand j'appelais quelque chose dans shouldAutorotate et j'ai ajouté du code à viewWillAppear
pour essayer de le réparer. Honnêtement, je ne me souviens pas si cela a fonctionné et je n'ai plus de Mac pour l'essayer, mais j'ai trouvé le code et je le collerai ici au cas où cela donnerait de l'inspiration.
- (void)viewWillAppear:(BOOL)animated{
UIInterfaceOrientation o;
switch ([UIDevice currentDevice].orientation) {
case UIDeviceOrientationPortrait:
o = UIInterfaceOrientationPortrait;
break;
case UIDeviceOrientationLandscapeLeft:
o = UIInterfaceOrientationLandscapeLeft;
break;
case UIDeviceOrientationLandscapeRight:
o = UIInterfaceOrientationLandscapeRight;
break;
default:
break;
}
[self shouldAutorotateToInterfaceOrientation:o];
}
NE PAS UTILISER CE HACK, APPLE REJETTERA L'APPLICATION BASÉE SUR L'UTILISATION DE 'API PRIVÉE'
Pour référence, je vais laisser ma réponse ici, mais l'utilisation de L'API privée ne passera pas devant le Comité d'examen. J'ai appris quelque chose aujourd'hui :d comme @younce a cité correctement les documents Apple, ce que je veux ne peut pas être réalisé avec UINavigationController.
J'avais deux options. Tout d'abord, j'aurais pu écrire mon propre substitut de contrôleur de navigation, avec toutes les horreurs que l'on aurait rencontré en le faisant. Deuxièmement, j'aurais pu pirater la rotation dans les contrôleurs de vue, en utilisant une fonctionnalité non documentée de UIDevice appelée setOrientation:animated:
Parce que la seconde était tentante facile, je suis allé pour celui-là. C'est ce que j'ai fait. Vous aurez besoin d'une catégorie pour supprimer les avertissements du compilateur concernant le setter qui n'existe pas:
@interface UIDevice (UndocumentedFeatures)
-(void)setOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated;
-(void)setOrientation:(UIInterfaceOrientation)orientation;
@end
Ensuite, vous devez vérifier les orientations prises en charge sur viewWillAppear:
. À côté des méthodes UIDevice utilisées ici, vous pouvez également forcer orientation portrait en présentant un contrôleur de vue modal, mais cela se produira instantanément et non animé, c'est donc ma façon préférée:
-(void)viewWillAppear:(BOOL)animated {
UIDevice *device = [UIDevice currentDevice];
UIDeviceOrientation realOrientation = device.orientation;
if ([self shouldAutorotateToInterfaceOrientation:realOrientation]) {
if (realOrientation != [UIApplication sharedApplication].statusBarOrientation) {
// Resetting the orientation will trigger the application to rotate
if ([device respondsToSelector:@selector(setOrientation:animated:)]) {
[device setOrientation:realOrientation animated:animated];
} else {
// Yes if Apple changes the implementation of this undocumented setter,
// we're back to square one.
}
}
} else if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationPortrait]) {
if ([device respondsToSelector:@selector(setOrientation:animated:)]) {
// Then set the desired orientation
[device setOrientation:UIDeviceOrientationPortrait animated:animated];
// And set the real orientation back, we don't want to truly mess with the iPhone's balance system.
// Because the view does not rotate on this orientation, it won't influence the app visually.
[device setOrientation:realOrientation animated:animated];
}
}
}
L'astuce consiste à toujours garder l'orientation interne du périphérique à l'orientation "réelle" du périphérique. Si vous commencez à changer cela, la rotation de votre application sera déséquilibrée.
, Mais comme je le sais maintenant, c'est juste un moyen sûr d'obtenir votre application rejeté. Donc, l'option numéro deux est juste une mauvaise option. Réécrire ce NavigationController, ou il suffit que toutes vos vues prennent en charge le même ensemble d'orientation.
Acclamations, EP.
Dans iOS 6, cela est devenu un problème très simple. Créez simplement une classe spéciale pour les vues que vous souhaitez autoroter. Ensuite, dans votre rootVC, ajoutez ceci.
-(BOOL)shouldAutorotate{
BOOL should = NO;
NSLog(@"%@", [self.viewControllers[self.viewControllers.count-1] class]);
if ([self.viewControllers[self.viewControllers.count-1] isKindOfClass:[YourAutorotatingClass class]]) {
should = YES;
}
return should;
}
Je sais que c'est une vieille question, mais j'ai pensé que cela valait la peine de le mentionner.