web-dev-qa-db-fra.com

Existe-t-il un moyen documenté de définir l'orientation de l'iPhone?

J'ai une application pour laquelle je souhaite prendre en charge la rotation des appareils dans certaines vues, mais d'autres n'ont pas de sens en mode Paysage. Par conséquent, lorsque je permute les vues, je souhaite que la rotation soit définie sur Portrait.

Il existe un configurateur de propriétés non documenté sur UIDevice qui fait l'affaire mais génère évidemment un avertissement du compilateur et pourrait disparaître avec une future révision du SDK.

[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];

Existe-t-il des moyens documentés pour forcer l'orientation?

Mise à jour: / Je pensais donner un exemple car je ne cherchais pas ShouldAutorotateToInterfaceOrientation car je l'ai déjà implémenté.

Je souhaite que mon application prenne en charge paysage et portrait dans View 1 mais uniquement portrait dans View 2. J'ai déjà implémenté shouldAutorotateToInterfaceOrientation pour toutes les vues, mais si l'utilisateur est en mode paysage dans View 1, puis passe à View 2, je souhaite forcer la téléphone pour revenir à Portrait.

94
Dave Verwer

Ce problème ne concerne plus le dernier SDK pour iPhone 3.1.2. Il semble maintenant tenir compte de l'orientation souhaitée de la vue repoussée dans la pile. Cela signifie probablement que vous devez détecter les anciennes versions du système d'exploitation iPhone et appliquer uniquement setOrientation avant la dernière version.

Il est difficile de savoir si l'analyse statique d'Apple comprendra que vous travaillez avec les anciennes limitations du SDK. Personnellement, Apple m'a dit de supprimer l'appel de méthode lors de ma prochaine mise à jour. Je ne sais donc pas encore si le piratage d'anciens appareils passera à travers le processus d'approbation.

6
John K

C'est longtemps après les faits, mais juste au cas où quelqu'un n'utiliserait pas de contrôleur de navigation et/ou ne souhaiterait pas utiliser de méthodes non documentées

UIViewController *c = [[UIViewController alloc]init];
[self presentModalViewController:c animated:NO];
[self dismissModalViewControllerAnimated:NO];
[c release];

Il suffit de présenter et de rejeter un contrôleur de vue Vanilla.

De toute évidence, vous devrez toujours confirmer ou infirmer l'orientation dans votre remplacement de shouldAutorotateToInterfaceOrientation. Mais cela entraînera la nécessité pour Autorotate ... d'être rappelé par le système.

101
Josh

D'après ce que je peux dire, la méthode setOrientation: ne fonctionne pas (ou ne fonctionne peut-être plus). Voici ce que je fais pour faire ceci:

tout d’abord, placez cette définition en haut de votre fichier, juste sous vos #imports:

#define degreesToRadian(x) (M_PI * (x) / 180.0)

puis, dans la méthode viewWillAppear:

[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];     
if (self.interfaceOrientation == UIInterfaceOrientationPortrait) {  
    self.view.transform = CGAffineTransformIdentity;
    self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
    self.view.bounds = CGRectMake(0.0, 0.0, 480, 320);
}

si vous voulez que cela soit animé, alors vous pouvez envelopper le tout dans un bloc d'animation, comme ceci:

[UIView beginAnimations:@"View Flip" context:nil];
[UIView setAnimationDuration:1.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];     
if (self.interfaceOrientation == UIInterfaceOrientationPortrait) {  
    self.view.transform = CGAffineTransformIdentity;
    self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
    self.view.bounds = CGRectMake(0.0, 0.0, 480, 320);
}
[UIView commitAnimations];

Ensuite, dans votre contrôleur en mode portrait, vous pouvez effectuer la procédure inverse - vérifiez si elle est actuellement en mode paysage et, le cas échéant, faites-la revenir à Portrait.

16
Bdebeez

Si vous voulez le forcer à tourner de portrait en paysage, voici le code. Notez simplement que vous devez ajuster le centre de votre vue. J'ai remarqué que le mien n'a pas placé la vue au bon endroit. Sinon, cela a fonctionné parfaitement. Merci pour le conseil.

if(UIInterfaceOrientationIsLandscape(self.interfaceOrientation)){

        [UIView beginAnimations:@"View Flip" context:nil];
        [UIView setAnimationDuration:0.5f];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

        self.view.transform = CGAffineTransformIdentity;
        self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
        self.view.bounds = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f);
        self.view.center = CGPointMake(160.0f, 240.0f);

        [UIView commitAnimations];
}
16
Michael Gaylord

J'ai creusé et creusé à la recherche d'une bonne solution à ce problème. Vous avez trouvé cet article de blog qui fait l'affaire: supprimez votre vue la plus externe de la clé UIWindow et ajoutez-la à nouveau, le système interrogera à nouveau les méthodes shouldAutorotateToInterfaceOrientation: de vos contrôleurs de vue, en appliquant l'orientation correcte à appliquer . iphone forçant uiview à se réorienter

7
nobre

Il existe un moyen simple de forcer par programme l’iPhone à l’orientation nécessaire - en utilisant deux des réponses déjà fournies par kdbdallas , Josh :

//will rotate status bar
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight];


//will re-rotate view according to statusbar
UIViewController *c = [[UIViewController alloc]init];
[self presentModalViewController:c animated:NO];
[self dismissModalViewControllerAnimated:NO];
[c release];

fonctionne comme un charme :)

MODIFIER:

pour iOS 6, j'ai besoin d'ajouter cette fonction: (fonctionne sur le contrôleur de vue modal)

- (NSUInteger)supportedInterfaceOrientations
{
    return (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight);
}
7

J'avais un problème où j'avais une UIViewController à l'écran, une UINavigationController, en orientation paysage. Cependant, lorsque le contrôleur de vue suivant est inséré dans le flux, il fallait que le périphérique revienne en orientation portrait.

Ce que j'ai remarqué, c'est que la méthode shouldAutorotateToInterfaceOrientation: n'est pas appelée lorsqu'un nouveau contrôleur de vue est insérée dans la pile, mais que est appelée lorsqu'un contrôleur de vue est extrait de la pile.

Profitant de cela, j'utilise cet extrait de code dans l'une de mes applications:

- (void)selectHostingAtIndex:(int)hostingIndex {

    self.transitioning = YES;

    UIViewController *garbageController = [[[UIViewController alloc] init] autorelease];
    [self.navigationController pushViewController:garbageController animated:NO];
    [self.navigationController popViewControllerAnimated:NO];

    BBHostingController *hostingController = [[BBHostingController alloc] init];
    hostingController.hosting = [self.hostings objectAtIndex:hostingIndex];
    [self.navigationController pushViewController:hostingController animated:YES];
    [hostingController release];

    self.transitioning = NO;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    if (self.transitioning)
        return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
    else
        return YES;
}

Fondamentalement, en créant un contrôleur de vue vide, en le plaçant sur la pile et en le supprimant immédiatement, il est possible de faire revenir l'interface à la position portrait. Une fois que le contrôleur a été ouvert, je viens d'appuyer sur le contrôleur que j'avais l'intention de pousser en premier lieu. Visuellement, ça a l'air génial - le contrôleur de vue vide et arbitraire n'est jamais vu par l'utilisateur.

7
Andrew Vilcsak

La réponse de Josh fonctionne bien pour moi.

Cependant, je préfère publier une "orientation modifiée, veuillez mettre à jour la notification" de l'interface utilisateur. Lorsque cette notification est reçue par un contrôleur de vue, il appelle shouldAutorotateToInterfaceOrientation:, ce qui vous permet de définir une orientation en renvoyant YES pour l'orientation souhaitée.

[[NSNotificationCenter defaultCenter] postNotificationName:UIDeviceOrientationDidChangeNotification object:nil];

Le seul problème est que cela force une ré-orientation sans une animation. Vous devez insérer cette ligne entre beginAnimations: et commitAnimations pour obtenir une transition en douceur.

J'espère que cela pourra aider.

5
Christian Schnorr

FWIW, voici ma mise en œuvre de la définition manuelle de l'orientation (pour aller dans le contrôleur de vue racine de votre application, natch): 

-(void)rotateInterfaceToOrientation:(UIDeviceOrientation)orientation{

    CGRect bounds = [[ UIScreen mainScreen ] bounds ];
    CGAffineTransform t;
    CGFloat r = 0;
    switch ( orientation ) {
        case UIDeviceOrientationLandscapeRight:
            r = -(M_PI / 2);
            break;
        case UIDeviceOrientationLandscapeLeft:
            r  = M_PI / 2;
            break;
    }
    if( r != 0 ){
        CGSize sz = bounds.size;
        bounds.size.width = sz.height;
        bounds.size.height = sz.width;
    }
    t = CGAffineTransformMakeRotation( r );

    UIApplication *application = [ UIApplication sharedApplication ];

    [ UIView beginAnimations:@"InterfaceOrientation" context: nil ];
    [ UIView setAnimationDuration: [ application statusBarOrientationAnimationDuration ] ];
    self.view.transform = t;
    self.view.bounds = bounds;
    [ UIView commitAnimations ];

    [ application setStatusBarOrientation: orientation animated: YES ];     
}

couplé à la méthode UINavigationControllerDelegate suivante (en supposant que vous utilisez un UINavigationController):

-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
    // rotate interface, if we need to
    UIDeviceOrientation orientation = [[ UIDevice currentDevice ] orientation ];
    BOOL bViewControllerDoesSupportCurrentOrientation = [ viewController shouldAutorotateToInterfaceOrientation: orientation ];
    if( !bViewControllerDoesSupportCurrentOrientation ){
        [ self rotateInterfaceToOrientation: UIDeviceOrientationPortrait ];
    }
}

Cela permet de faire pivoter la vue racine en fonction de si une UIViewController entrante prend en charge l'orientation actuelle du périphérique. Enfin, vous voudrez associer rotateInterfaceToOrientation aux changements d'orientation de périphérique afin de reproduire les fonctionnalités iOS standard. Ajoutez ce gestionnaire d'événements au même contrôleur de vue racine:

-(void)onUIDeviceOrientationDidChangeNotification:(NSNotification*)notification{
    UIViewController *tvc = self.rootNavigationController.topViewController;
    UIDeviceOrientation orientation = [[ UIDevice currentDevice ] orientation ];
    // only switch if we need to (seem to get multiple notifications on device)
    if( orientation != [[ UIApplication sharedApplication ] statusBarOrientation ] ){
        if( [ tvc shouldAutorotateToInterfaceOrientation: orientation ] ){
            [ self rotateInterfaceToOrientation: orientation ];
        }
    }
}

Enfin, inscrivez-vous aux notifications UIDeviceOrientationDidChangeNotification dans init ou loadview comme ceci:

[[ NSNotificationCenter defaultCenter ] addObserver: self
                                           selector: @selector(onUIDeviceOrientationDidChangeNotification:)
                                               name: UIDeviceOrientationDidChangeNotification
                                             object: nil ];
[[ UIDevice currentDevice ] beginGeneratingDeviceOrientationNotifications ];
3
Henry Cooke

Cela fonctionne pour moi (merci Henry Cooke):

Le but pour moi était de traiter uniquement des changements d’orientation du paysage.

méthode init:

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(orientationChanged:)
                                             name:UIDeviceOrientationDidChangeNotification
                                           object:nil];

- (void)orientationChanged:(NSNotification *)notification {    
    //[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
    UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
    CGRect bounds = [[ UIScreen mainScreen ] bounds ];
    CGAffineTransform t;
    CGFloat r = 0;
    switch ( orientation ) {
        case UIDeviceOrientationLandscapeRight:
            r = 0;
            NSLog(@"Right");
            break;
        case UIDeviceOrientationLandscapeLeft:
            r  = M_PI;
            NSLog(@"Left");
            break;
        default:return;
    }

    t = CGAffineTransformMakeRotation( r );

    UIApplication *application = [ UIApplication sharedApplication ];
    [ UIView beginAnimations:@"InterfaceOrientation" context: nil ];
    [ UIView setAnimationDuration: [ application statusBarOrientationAnimationDuration ] ];
    self.view.transform = t;
    self.view.bounds = bounds;
    [ UIView commitAnimations ];

    [ application setStatusBarOrientation: orientation animated: YES ];
}
2
user324168

J'ai une application pour laquelle je souhaite prendre en charge la rotation des appareils dans certaines vues, mais d'autres n'ont pas de sens en mode Paysage. Par conséquent, lorsque je permute les vues, je souhaite forcer la rotation de la rotation à portrait.

Je me rends compte que le message original ci-dessus dans ce fil est très ancien maintenant, mais j'ai eu un problème similaire - c'est-à-dire. tous les écrans de mon application sont uniquement en mode portrait, à l'exception d'un écran pouvant être pivoté entre paysage et portrait par l'utilisateur.

C’était assez simple, mais comme d’autres publications, je voulais que l’application revienne automatiquement en mode portrait quelle que soit l’orientation actuelle de l’appareil, lors du retour à l’écran précédent.

La solution que j'ai mise en place consistait à masquer la barre de navigation en mode paysage, ce qui signifie que l'utilisateur ne peut revenir aux écrans précédents qu'en mode portrait. Par conséquent, tous les autres écrans ne peuvent être qu'en portrait.

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)pInterfaceOrientation {
    BOOL lHideNavBar = self.interfaceOrientation == UIInterfaceOrientationPortrait ? NO : YES;
    [self.navigationController setNavigationBarHidden:lHideNavBar animated:YES];
}

Cela présente également un avantage supplémentaire pour mon application, en ce sens qu'il y a davantage d'espace d'écran disponible en mode paysage. Ceci est utile car l'écran en question est utilisé pour afficher les fichiers PDF.

J'espère que cela t'aides.

1
stephencampbell

solution iOS 6:

[[[self window] rootViewController] presentViewController:[[UIViewController alloc] init] animated:NO completion:^{
    [[[self window] rootViewController] dismissViewControllerAnimated:NO completion:nil];
}];

Le code exact dépend de chaque application et de son emplacement (je l'ai utilisé dans mon AppDelegate). Remplacez [[self window] rootViewController] par ce que vous utilisez. J'utilisais une UITabBarController.

1
Bo A

Je l'ai résolu assez facilement à la fin. J'ai essayé toutes les suggestions ci-dessus et je suis toujours tombé à court, alors voici ma solution:

Dans le ViewController qui doit rester Paysage (Gauche ou Droite), j'écoute les changements d'orientation:

    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(didRotate:)
                                             name:UIDeviceOrientationDidChangeNotification object:nil];

Puis dans didRotate:

- (void) didRotate:(NSNotification *)notification
{   if (orientationa == UIDeviceOrientationPortrait) 
    {
        if (hasRotated == NO) 
        {
            NSLog(@"Rotating to portait");
            hasRotated = YES;
            [UIView beginAnimations: @"" context:nil];
            [UIView setAnimationDuration: 0];
            self.view.transform = CGAffineTransformIdentity;
            self.view.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(-90));
            self.view.bounds = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f);
            self.view.frame = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f);
            [UIView commitAnimations];

    }
}
else if (UIDeviceOrientationIsLandscape( orientationa))
{
    if (hasRotated) 
    {
        NSLog(@"Rotating to lands");
        hasRotated = NO;
        [UIView beginAnimations: @"" context:nil];
        [UIView setAnimationDuration: 0];
        self.view.transform = CGAffineTransformIdentity;
        self.view.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(0));
        self.view.bounds = CGRectMake(0.0f, 0.0f, 320.0f, 480.0f);
        self.view.frame = CGRectMake(0.0f, 0.0f, 320.0f, 480.0f);
        [UIView commitAnimations];

    }
}

Gardez à l'esprit les super vues/sous-vues qui utilisent le redimensionnement automatique, car view.bounds/frame sont réinitialisés explicitement ... 

Le seul inconvénient de cette méthode pour conserver la vue Paysage est la commutation d’animation inhérente entre les orientations qui doit se produire, alors qu’il serait préférable de ne pas modifier l’image. 

1
mrdavenz

J'ai trouvé une solution et écrit quelque chose en français (mais le code est en anglais). ici

Le moyen est d’ajouter le contrôleur à la fenêtre (le contrôleur doit posséder une bonne implémentation de la fonction shouldRotate ....).

0
Vinzius