Question UINavigationController et autorotation


J'ai un UIViewController qui retourne YES dans shouldAutorotateToInterfaceOrientation: pour UIDeviceOrientationPortrait et NO pour tout le reste. Avec cette vue sur le dessus de la pile, j'utilise pushViewController:animated: pousser un nouveau UIViewController. Le nouveau contrôleur revient YES pour rien dans shouldAutorotateToInterfaceOrientation:.

La première vue refuse de tourner (comme prévu). Une fois que la seconde vue est enfoncée, l'utilisateur peut faire pivoter le périphérique et l'interface utilisateur tournera (également comme prévu). Si la deuxième vue est en mode paysage et que l'utilisateur appuie sur le bouton retour (qui appelle popViewControllerAnimated:), la première vue apparaîtra tournée (inattendu!).

Si l'utilisateur retourne l'appareil en mode portrait, la vue pivotera et sera ensuite bloquée en mode portrait comme précédemment. Cela fonctionne, mais c'est moche pour l'utilisateur jusqu'à ce qu'ils tournent en arrière. Je suis donc à la recherche d'un moyen pour que cette vue reste en mode portrait.

La seule solution de contournement que j'ai trouvée jusqu'à présent est d'utiliser -[UIDevice setOrientation:], qui lance un avertissement (orientation est en lecture seule) mais fonctionne depuis sa définition. C'est un énorme hack et j'aimerais une vraie solution. A la recherche d'une solution réelle, j'ai attaché GDB à l'application Photos (MobileSlideshow.app) et j'ai découvert qu'elle -[UIDevice setOrientation:]. Étant une application interne, je suppose qu'ils ont des règles différentes.

Existe-t-il une manière correcte d’atteindre le comportement d’autorotation attendu?


24
2018-06-09 14:45


origine


Réponses:


J'étais sur le point de vous dire qu'il n'y avait probablement aucun moyen, mais alors j'ai eu une pensée. Il serait difficile de bien faire, mais vous pourriez être en mesure de le faire fonctionner si vous avez utilisé deux UINavigationControllers: un contrôle la vue racine et interdit la rotation, et un pour les vues enfants qui le permettent. Vous devez gérer manuellement la transition vers et depuis le contrôleur racine et le contrôleur enfant.

Vous devez réparer le contrôleur de navigation enfant pour avoir le bon bouton de retour. Et, bien sûr, vous devez vous occuper de la pression sur le bouton retour. Vous devrez probablement utiliser un mannequin UINavigationBar faire l'animation d'un contrôleur de navigation à l'autre pour que la transition soit correcte. Vous devrez également animer la transition "push" entre les contrôleurs de navigation, ce qui pourrait nécessiter quelques ajustements pour que le résultat soit correct. Vous auriez à:

  1. Configurez une barre de navigation factice pour correspondre exactement au contrôleur de navigation sortant et placez-la directement au-dessus de la barre du contrôleur de navigation. (Vous pouvez copier la configuration du contrôleur de vue actuel UINavigationItem et poussez dessus)
  2. Placez le nouveau contrôleur de navigation hors écran au bord droit
  3. Animer le mouvement des cadres des anciens et nouveaux contrôleurs de droite à gauche
  4. Créer une copie du UINavigationItem pour le contrôleur de vue entrant et le pousser sur la barre de navigation factice
  5. Une fois l'animation terminée, retirez le mannequin UINavigationBar depuis la vue, ainsi que le contrôleur de navigation sortant.

Tout cela demande beaucoup de travail, mais si vous êtes très intelligent (et très tenace), vous pourrez peut-être le faire fonctionner. J'adorerais voir le résultat!

Cela dit, vous feriez mieux d’utiliser simplement setOrientation: et en prenant vos chances avec le processus d'approbation App Store ;-)


1
2018-06-09 15:38



Une alternative légale à UIDevice setOrientation est la suivante:

UIWindow* window = UIApplication.sharedApplication.keyWindow;
UIView* view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window addSubview:view];

Cela force le contrôleur de vue actuel à évaluer son orientation, en appelant shouldAutorotateToInterfaceOrientation et en évitant toute orientation interdite.

Par exemple. le code suivant serait utilisé dans un contrôleur de vue qui prend en charge toutes les orientations, mais dont le parent ne prend en charge que le paysage:

- (void)popBack
{
    [self.navigationController popToRootViewControllerAnimated:YES]; 
}

- (IBAction)backPressed:(id)sender
{   
    portraitOrientationPermitted = NO;

    // Force the framework to re-evaluate the interface orientation.
    UIWindow* window = UIApplication.sharedApplication.keyWindow;
    UIView* view = [window.subviews objectAtIndex:0];
    [view removeFromSuperview];
    [window addSubview:view];

    [self performSelector:@selector(popBack) withObject:nil afterDelay:0.8];

    portraitOrientationPermitted = YES;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return portraitOrientationPermitted || 
        UIInterfaceOrientationIsLandscape(interfaceOrientation);
}

6
2017-11-14 20:15



Il a été un ancien message mais comme il n'a pas été résolu. Je voudrais partager ma solution pour tous ceux qui pourraient avoir mal à la tête.

Objectif: Un UINavigationController et la plupart des viewcontrollers de sa pile sont fixés en mode portrait, à l'exception d'un contrôleur de vue dans la pile qui peut pivoter en mode portrait et paysage.

Problème: intuitivement, je mets en place une méthode sélective shouldAutorotateToInterfaceOrientation en vérifiant si topViewController est le rotableViewController. Cependant, après être revenu du rotableViewController en mode paysage, le contrôleur de navigation est maintenant affiché en mode paysage, bien que cela ne soit pas autorisé.

Solution: Le tueur doit interdire la rotation à viewWillAppear et présenter et rejeter un modalViewController sans animation.

  1. Un appViewController est ajouté à la fenêtre en tant qu'hôte viewController, c'est-à-dire rooter que le RootViewController;
  2. Un navigateur de navigation est ajouté à appViewController, avec déléguer la valeur à appViewController;
  3. Dans AppViewController


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    if (interfaceOrientation == UIInterfaceOrientationPortrait) return YES;
    return canRotate;
}


- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    [viewController viewDidAppear:animated];
    canRotate = ([navigationController.topViewController isKindOfClass:[MyRotatable class]]);
}


- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    [viewController viewWillAppear:animated];
    if (![navigationController.topViewController isKindOfClass:[MyRotatable class]]) {
        canRotate = NO;
        UIViewController * blanck = [[UIViewController alloc] initWithNibName:nil bundle:nil];
        [self presentModalViewController:blanck animated:NO];
        [self dismissModalViewControllerAnimated:NO];
        [blanck release];
    }
}

5
2018-03-18 01:22



iOS 5 ajoute + [UIViewController tryRotationToDeviceOrientation], ce qui résout le problème pour moi.


4
2017-09-20 22:17



Essayez à nouveau avec le 3.0 OS (maintenant que nous pouvons en parler). Deux cas particuliers ont été mis en évidence sous 3.0:

  1. Présenter une vue modale de portrait sur une vue de paysage et vice-versa. Un exemple est le comportement pré-3.0 dans Mail pour l'affichage des pièces jointes. Vous pouvez effectuer une rotation vers un paysage pour un PDF et récupérer la vue de portrait pour le message lorsque vous avez rejeté la vue de pièce jointe. (Maintenant que nous avons une vue de message paysage en 3.0, ce comportement semble avoir disparu).

  2. Mélanger les orientations dans une vue s'empiler et obtenir l'orientation correcte lorsque vous sautez la pile. Un exemple est la transition entre la vue de table de films et la vue de film dans l'application YouTube.

Il semble y avoir eu des problèmes esthétiques épineux de la façon dont les transitions par défaut pour toutes les permutations devraient ressembler. Il y a des éléments bizarres si vous les visualisez dans slo-mo, mais cela bat en arrière dans votre propre code.


1
2018-06-17 18:52



J'ai trouvé une bonne solution pour résoudre ce problème. La clé est de soutenir tout orientations pour tout vues en UINavigationController.

J'ai 2 vues dans le contrôleur. La vue racine ne prend en charge que LandscapeRight, et second supporte les deux LandscapeRight et Portrait.

Deuxième vue shouldAutorotateToInterfaceOrientation la méthode a l'air comme d'habitude:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationLandscapeRight) ||
           (interfaceOrientation == UIInterfaceOrientationPortrait);
}

La solution elle-même est contenue dans la source d'affichage racine La vue racine tourne maintenant en termes de code, mais l'utilisateur ne peut pas la voir.

//auxiliary function
-(void) fixOrientation:(UIInterfaceOrientation)orientation
{
    if (orientation == UIInterfaceOrientationPortrait)
        self.view.transform = CGAffineTransformMakeRotation(M_PI_2);
    else if (orientation == UIInterfaceOrientationLandscapeRight)
        self.view.transform = CGAffineTransformMakeRotation(0);
}

-(void) viewWillAppear:(BOOL)animated
{
    [self fixOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    [self fixOrientation:interfaceOrientation];
    //notice, that both orientations are accepted
    return (interfaceOrientation == UIInterfaceOrientationLandscapeRight) ||
           (interfaceOrientation == UIInterfaceOrientationPortrait);
}

//these two functions helps to avoid blinking
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [UIView setAnimationsEnabled:NO]; // disable animations temporarily

}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [UIView setAnimationsEnabled:YES]; // rotation finished, re-enable them
}

1
2017-08-08 06:57



Donc, ce bug ennuyeux arrive très longtemps de iOS 1 à iOS 4.

Je crois que la meilleure solution que nous ayons en double ce bug et faire savoir à Apple que nous voulons vraiment qu’il soit réparé. Je viens juste de le signaler sous l'ID de bogue 8478525.


0
2017-09-25 17:01