Esiste un modo documentato per impostare l'orientamento dell'iPhone?


94

Ho un'app in cui vorrei supportare la rotazione del dispositivo in determinate visualizzazioni, ma altre non hanno particolarmente senso in modalità Landscape, quindi mentre cambio le visualizzazioni vorrei forzare la rotazione in verticale.

C'è un setter di proprietà non documentato su UIDevice che fa il trucco ma ovviamente genera un avviso del compilatore e potrebbe scomparire con una futura revisione dell'SDK.

[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];

Esistono modi documentati per forzare l'orientamento?

Aggiornamento: ho pensato di fornire un esempio in quanto non sto cercando shouldAutorotateToInterfaceOrientation poiché l'ho già implementato.

Voglio che la mia app supporti il ​​paesaggio e il ritratto nella Vista 1 ma solo il ritratto nella Vista 2. Ho già implementato shouldAutorotateToInterfaceOrientation per tutte le viste ma se l'utente è in modalità orizzontale nella Vista 1 e poi passa alla Vista 2, voglio forzare il telefono per ruotare di nuovo in verticale.


11
Ho segnalato un bug su questo, suggerendo ad Apple di esporre l'API sopra, o di onorare il SÌ, NO restituito dal metodo shouldRotate, quando viene caricata per la prima volta una vista, non solo quando il telefono ruota.
rustyshelf

2
Ho segnalato un bug chiedendo che setOrientation fosse esposto
Jessedc

1
[[UIDevice currentDevice] setOrientation: UIInterfaceOrientationPortrait]; Questo metodo è deprecato e non esiste più.
Hikmat Khan

Risposte:


6

Questo non è più un problema sul successivo iPhone 3.1.2 SDK. Ora sembra onorare l'orientamento richiesto della vista che viene reinserita nella pila. Ciò probabilmente significa che dovresti rilevare le versioni precedenti di iPhone OS e applicare il setOrientation solo quando è precedente all'ultima versione.

Non è chiaro se l'analisi statica di Apple capirà che stai aggirando le limitazioni dell'SDK meno recenti. Personalmente mi è stato detto da Apple di rimuovere la chiamata al metodo al mio prossimo aggiornamento, quindi non sono ancora sicuro se un hack per dispositivi più vecchi supererà il processo di approvazione.


Non è il caso di iPhone Simulator 3.3.2. Ancora non funziona.
Duck

4
@user Come si richiede un orientamento quando si inserisce una vista nello stack?
programma

4
Puoi aggirare l'analisi statica di Apple. Uso con successo codice non documentato utilizzando performSelector e affidandomi al fantastico dinamismo di Obj-C.
Kenneth Ballenegger

@KennethBallenegger Ricordo di aver letto nelle linee guida e / o nell'accordo che nascondere cose del genere può far rimuovere la tua app e forse anche il tuo account. Non lo rischierei solo per usare API non documentate.
Ivan Vučica

101

Questo è molto tempo dopo il fatto, ma nel caso in cui qualcuno si presenti che non utilizza un controller di navigazione e / o non desidera utilizzare metodi non documentati:

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

È sufficiente presentare e chiudere un controller di visualizzazione vanilla.

Ovviamente dovrai comunque confermare o negare l'orientamento nel tuo override di shouldAutorotateToInterfaceOrientation. Ma questo farà sì che shouldAutorotate ... venga nuovamente chiamato dal sistema.


1
Fantastico, questa non è contrassegnata come la risposta corretta di questo post, ma ha risolto metà del mio problema stackoverflow.com/questions/5030315 . Il trucco della vista modello "forza" l'orientamento verticale, ne conosceresti anche uno per forzare il paesaggio?
epologo

5
Hah hah !! questo è semplicemente troppo stupido! MA FUNZIONA! - certo che lo fa! : D
hfossli

Fantastico, ho lottato con questo per molto tempo. Grazie! amico
Rahul Choudhary

3
Questo sembra funzionare sia per iOS5 che per iOS6: [self presentViewController: [UIViewController new] animated: NO completamento: ^ {[self dismissViewControllerAnimated: NO completamento: nil]; }];
Inferis

1
Sembra funzionare su iOS 7 e iOS 8, ma c'è una notevole schermata nera che lampeggia (principalmente iOS 8).
levigroker

16

Se vuoi forzare la rotazione da verticale a orizzontale, ecco il codice. Tieni presente che devi regolare il centro della tua vista. Ho notato che la mia non posizionava la vista nel posto giusto. Altrimenti, ha funzionato perfettamente. Grazie per il consiglio.

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];
}

Funzionerà anche se ho aggiunto un UIAlertView? Ho provato CGAffineTransformMakeRotationa ruotare la vista ma UIAlertView è ancora in base all'orientamento della barra di stato, ad esempio -degreeToRadian (angolo)? Hai preso qualcosa di simile?
NeverHopeless

Il modo più semplice per farlo con a UIAlertViewè impostare il controller della vista come delegato di alertView e quindi su:, - (void) didPresentAlertView:(UIAlertView *) alertViewsi esegue la rotazione in base alla corrente [UIApplication sharedApplication].statusBarOrientation. Se non lo animi, non dovrebbe sembrare troppo strano.
Michael Gaylord

16

Da quello che posso dire, il file setOrientation: metodo non funziona (o forse non funziona più). Ecco cosa sto facendo per farlo:

per prima cosa, metti questa definizione all'inizio del tuo file, proprio sotto le tue #import:

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

poi, nel viewWillAppear:metodo

[[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);
}

se vuoi che sia animato, puoi avvolgere il tutto in un blocco di animazione, in questo modo:

[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];

Quindi, nel controller della modalità verticale, puoi fare il contrario: controlla se è attualmente in orizzontale e, in tal caso, ruotalo di nuovo su Verticale.


1
Invece di self.view dovresti ruotare self.window.view. Questo è utile se hai UITabBar o altri controller in cima alla tua vista.
Borut Tomazin

7

Stavo riscontrando un problema a causa del quale UIViewControllersullo schermo era visualizzato un UINavigationController, in orientamento orizzontale. Quando il controller di visualizzazione successivo viene inserito nel flusso, tuttavia, avevo bisogno che il dispositivo tornasse all'orientamento verticale.

Quello che ho notato è che il shouldAutorotateToInterfaceOrientation:metodo non viene chiamato quando un nuovo controller di visualizzazione viene inserito nello stack, ma viene chiamato quando un controller di visualizzazione viene estratto dallo stack .

Approfittando di questo, sto usando questo frammento di codice in una delle mie app:

- (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;
}

Fondamentalmente, creando un controller di visualizzazione vuoto, inserendolo nello stack e rimuovendolo immediatamente, è possibile riportare l'interfaccia in posizione verticale. Una volta che il controller è stato aperto, spingo semplicemente il controller che intendevo spingere in primo luogo. Visivamente, sembra fantastico: il controller di visualizzazione vuoto e arbitrario non viene mai visto dall'utente.


Ciao, ho appena implementato il tuo metodo per forzare un VC nell'orientamento corretto prima di spingerlo, ma ho scoperto che funziona solo forzando gli orientamenti Verticali (se la vista corrente è Verticale e desideri forzare la successiva in orizzontale, ha vinto chiamare shouldAutorotateToInterfaceOrientation: quando si apre). Hai trovato una soluzione alternativa per forzare una vista su Landscape?
Rafael Nobre

Interessante: non ho avuto questo problema, mi dispiace. Proverei a giocare con la sequenza di spinte e schiocchi, c'è una buona possibilità che ti venga in mente qualcosa.
Andrew Vilcsak

7

C'è un modo semplice per forzare programmaticamente l'iPhone all'orientamento necessario, utilizzando due delle risposte già fornite da 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];

funziona come un incantesimo :)

MODIFICARE:

per iOS 6 devo aggiungere questa funzione: (funziona su viewcontroller modale)

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

Per me non funziona come un incantesimo: la barra di navigazione e la barra degli strumenti si restringono fino all'altezza che avrebbero nella vista Paesaggio. Il metodo con la rimozione e l'aggiunta di nuovo della vista in alto mantiene l'altezza corretta, ma ha anche uno svantaggio: la mia barra di navigazione si sposta leggermente verso il basso (come se la barra di registrazione fosse abilitata)
AlexWien

Funziona da parte mia, sia su viewcontroller modale che pushed. Ma solo su iOS 5. :(
Guntis Treulands

Sì, devo farlo funzionare anche in iOS5. Ora in ios6 l'altezza della barra di navigazione si riduce una volta che il controller di visualizzazione orizzontale modale viene
chiuso

7

Ho scavato e scavato cercando una buona soluzione a questo. Ho trovato questo post del blog che fa il trucco: rimuovi la tua vista più esterna dalla chiave UIWindowe aggiungila di nuovo, il sistema interrogherà nuovamente i shouldAutorotateToInterfaceOrientation:metodi dai tuoi viewcontroller, imponendo l'orientamento corretto da applicare. Guardalo : iphone costringe uiview a riorientarsi


Questa soluzione ha funzionato meglio per me. È importante sottolineare che la correzione che presenta un modalViewController vuoto causa un'animazione errata del push dei viewcontroller nello stack. Questo metodo UIWindow non soffre dello stesso problema.
Rik Smith-Unna

5

La risposta di Josh funziona bene per me.

Tuttavia, preferisco pubblicare una notifica "l'orientamento è cambiato, aggiorna l'interfaccia utente" . Quando questa notifica viene ricevuta da un controller di visualizzazione, chiama shouldAutorotateToInterfaceOrientation:, consentendo di impostare qualsiasi orientamento tornando YESper l'orientamento desiderato.

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

L'unico problema è che questo forza un riorientamento senza un'animazione. Avresti bisogno di racchiudere questa linea tra beginAnimations:ecommitAnimations per ottenere una transizione graduale.

Spero che aiuti.


3

FWIW, ecco la mia implementazione dell'impostazione manuale dell'orientamento (per andare nel controller di visualizzazione principale della tua app, 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 ];     
}

accoppiato con il seguente UINavigationControllerDelegatemetodo (supponendo che tu stia usando a 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 ];
    }
}

Ciò si occupa di ruotare la vista root a seconda che un ingresso UIViewControllersupporti l'orientamento del dispositivo corrente. Infine, ti consigliamo di collegarti rotateInterfaceToOrientationalle effettive modifiche dell'orientamento del dispositivo per imitare la funzionalità iOS standard. Aggiungi questo gestore eventi allo stesso controller di visualizzazione radice:

-(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 ];
        }
    }
}

Infine, registrati per le UIDeviceOrientationDidChangeNotificationnotifiche in inito in questo loadviewmodo:

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

Questa è l'unica delle risposte in questa pagina che sono riuscito ad adattare con successo al codice del mio controller di navigazione. L'unico problema minore è che la barra di navigazione ruotata non cambia l'altezza tra gli orientamenti verticale e orizzontale come farebbe se ruotata tramite il normale meccanismo.
Dan Dyer

@DanDyer hai risolto il problema con l'altezza sbagliata della barra di navigazione? (Soprattutto in iOS6 l'altezza è sbagliata)
AlexWien

@AlexWien Alla fine sono riuscito a convincere il cliente che non dovremmo farlo. Per iOS 6 potresti dover fare qualcosa con il nuovo shouldAutorotatemetodo (vedi stackoverflow.com/a/12586002/5171 ).
Dan Dyer

@DanDyer l'ha risolto ieri, un semplice nascondere e mostrare di navigazione e statusBar in ViewDidAppear lo ha risolto Ora tutto funziona (SDK ios6, target ios5) Devo ripetere il test con il target ios6
AlexWien

2

Questo funziona per me (grazie Henry Cooke):

Lo scopo per me era affrontare solo i cambiamenti di orientamento del paesaggio.

metodo 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 ];
}

1

Ho un'app in cui vorrei supportare la rotazione del dispositivo in determinate visualizzazioni, ma altre non hanno particolarmente senso in modalità Landscape, quindi mentre cambio le visualizzazioni vorrei forzare la rotazione in verticale.

Mi rendo conto che il post originale di cui sopra in questo thread è molto vecchio ora, ma ho avuto un problema simile ad esso - ad es. tutti gli schermi nella mia App sono solo verticali, ad eccezione di uno schermo, che può essere ruotato tra orizzontale e verticale dall'utente.

Questo è stato abbastanza semplice, ma come altri post, volevo che l'app tornasse automaticamente in verticale indipendentemente dall'orientamento del dispositivo corrente, quando si torna alla schermata precedente.

La soluzione che ho implementato è stata quella di nascondere la barra di navigazione in modalità orizzontale, il che significa che l'utente può tornare alle schermate precedenti solo in verticale. Pertanto, tutti gli altri schermi possono essere solo in verticale.

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

Questo ha anche il vantaggio aggiuntivo per la mia app in quanto c'è più spazio sullo schermo disponibile in modalità orizzontale. Ciò è utile perché la schermata in questione viene utilizzata per visualizzare i file PDF.

Spero che questo ti aiuti.


1

Alla fine l'ho risolto abbastanza facilmente. Ho provato tutti i suggerimenti di cui sopra e sono ancora venuto a mancare, quindi questa era la mia soluzione:

Nel ViewController che deve rimanere orizzontale (sinistro o destro), ascolto i cambiamenti di orientamento:

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

Quindi in 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];

    }
}

Tieni presente qualsiasi Super Visualizzazioni / Sottoviste che utilizzano il ridimensionamento automatico, poiché view.bounds / frame vengono reimpostati esplicitamente ...

L'unico avvertimento a questo metodo per mantenere la vista Paesaggio, è l'animazione intrinseca che passa da un orientamento all'altro che deve verificarsi, quando sarebbe meglio che sembri non avere cambiamenti.


1

Soluzione iOS 6:

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

Il codice esatto dipende dall'app e anche da dove lo posizioni (l'ho usato nel mio AppDelegate). Sostituisci [[self window] rootViewController]con quello che usi. Stavo usando un file UITabBarController.


0

Ho trovato una soluzione e ho scritto qualcosa in francese (ma il codice è in inglese). Qui

Il modo è aggiungere il controller alla visualizzazione della finestra (il controller deve possedere una buona implementazione della funzione shouldRotate ....).


-3

Se stai usando UIViewControllers, c'è questo metodo:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

Torna NOper i controller di visualizzazione che contengono le visualizzazioni che non desideri ruotare.

Maggiori info qui


1
Ciò interrompe la rotazione, quello che sto cercando è forzare una rotazione di nuovo in verticale se l'utente è in orizzontale. Ho aggiunto un esempio sopra per dire cosa intendo meglio.
Dave Verwer

-3

Non penso che questo sia possibile in fase di esecuzione, anche se ovviamente potresti semplicemente applicare una trasformazione di 90 gradi alla tua interfaccia utente.


È possibile con il codice che ho pubblicato originariamente, non sai mai quando potrebbe smettere di funzionare;)
Dave Verwer

-3

Questo è quello che uso. (Ricevi alcuni avvisi di compilazione ma funziona sia nel simulatore che nell'iPhone)

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight];
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];

Ho scoperto che questo è sufficiente per farlo funzionare anche in 4.1. Tuttavia, poiché la mia barra di stato è impostata su nascosta, guadagno 20 pixel di spazio nella parte superiore :(
Anthony Main

setOrientation è un'API privata e l'app che la utilizza viene rifiutata.
solo
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.