2011-09-08 14 views
17

Ho una gerarchia di viste nidificate per un'applicazione iPad che supporta le modifiche di orientamento. Sembra simile al seguente.iOS: disabilita l'autorotazione per una sottomaschera

UIViewController 
    UIView 
     - UIView 
      - UIImageView (disable rotation) 
      - UIImageView 
      - UIView (disable rotation) 
     - UIView 
     - UIView 
     ... 

Vorrei bloccare l'orientamento per alcuni dei miei subviews, permettendo ad altri di auto-rotazione e ridimensionamento. Non riesco proprio a capire come realizzare questo.

Un approccio sembra ruotare manualmente le sottoview entro willAnimateRotationToInterfaceOrientation:. Questo non è particolarmente interessante dato che l'SDK sta eseguendo una rotazione che vorrei solo annullare.

C'è un modo per disabilitare semplicemente le modifiche all'orientamento per le sottoview o qualche altro metodo per ristrutturare la mia gerarchia?

+0

Una soluzione semplice: http://stackoverflow.com/a/6292506/294884 – Fattie

risposta

22

L'autorotazione viene gestita da un UIViewController di una vista (shouldAutorotateToInterfaceOrientation:), pertanto un approccio consiste nell'ordinare la propria gerarchia in modo che le viste ruotabili siano gestite da un controller di visualizzazione e da viste non ruotabili da un altro controller di visualizzazione. Entrambe le viste radice di UIViewController devono quindi essere aggiunte alla finestra/superview.

La sottigliezza è che se si dispone di due viste vista del controller sullo stesso piano (vale a dire aggiunto tramite addSubview:), solo il primo controller della vista (di solito della finestra rootViewController) riceverà il messaggio shouldAutorotateToInterfaceOrientation:.

Ho utilizzato questo approccio da solo per ottenere una barra degli strumenti che ruota, mentre la vista principale no.

Apple's Technical Q&A QA1688 ("Why won't my UIViewController rotate with the device?") parla un po 'di questo problema.


Aggiornamento per iOS 6:

autorotazione ora utilizza shouldAutorotate e supportedInterfaceOrientations metodi UIViewController s'. shouldAutorotate restituisce YES per impostazione predefinita, ma ricorda che un controller di visualizzazione diverso da rootViewController la cui vista è una vista secondaria diretta della finestra NON riceverà comunque callback di rotazione.


Codice di esempio per iOS 6:

crea un nuovo progetto utilizzando il modello "Single View Application" e garantire "Usa storyboard" sia selezionata. Useremo la classe ViewController fornita come controller di visualizzazione rotante (rinominala se vuoi!) E creiamo una seconda sottoclasse UIViewController chiamata NonRotatingViewController. Anche se questo controller della vista sarà mai nemmeno ricevere i callback di rotazione, per completezza e chiarezza aggiungere il seguente codice nel NonRotatingViewController.m:

- (BOOL)shouldAutorotate 
{ 
    return NO; 
} 

Nel file MainStoryboard, trascinare un nuovo oggetto View Controller e impostare questa categoria per NonRotatingViewController, e imposta l'ID Storyboard su "NonRotatingVC". Mentre sei lì, cambia il colore di sfondo della vista del controller di visualizzazione rotante per cancellare (la vista non rotante verrà aggiunta al di sotto di questa) e aggiungere un'etichetta a ciascuna vista.In AppDelegate.m, aggiungere il codice seguente:

#import "NonRotatingViewController.h" 

// ... 
// ... 

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{ 
    // Override point for customization after application launch. 
    UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil]; 
    NonRotatingViewController *nonRotatingVC = [mainStoryboard instantiateViewControllerWithIdentifier:@"NonRotatingVC"]; 
    [self.window addSubview:nonRotatingVC.view]; 
    return YES; 
} 

Questo è solo istanziare un controller non vista ruotare e aggiungendo la sua vista direttamente alla finestra (N.B. a questo punto della finestra rootViewController già impostato dal storyboard).

Eseguire il progetto. Ruota il dispositivo e meraviglia alla vista di un'etichetta che ruota mentre l'altra rimane ferma!


codice di esempio pre iOS 6:

Ho fatto questo in un nuovo progetto - una nuova applicazione Visualizza-based farà bene. Aggiungi due nuovi controller di visualizzazione: RotatingViewController e NonRotatingViewController. All'interno di ciascuno dei loro pennini ho appena aggiunto un'etichetta per descrivere se la vista dovrebbe ruotare o meno. Aggiungere il seguente codice:

'RotatingViewController.m'

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 
{ 
    return YES; 
} 


'NonRotatingViewController.m'

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 
{ 
    if (interfaceOrientation == UIInterfaceOrientationPortrait) { // Or whatever orientation it will be presented in. 
     return YES; 
    } 
    return NO; 
} 


'AppDelegate.m'

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{ 
    RotatingViewController *rotating = [[RotatingViewController alloc] initWithNibName:@"RotatingViewController" bundle:nil]; 
    self.rotatingViewController = rotating; 
    [rotating release]; 

    NonRotatingViewController *nonRotating = [[NonRotatingViewController alloc] initWithNibName:@"NonRotatingViewController" bundle:nil]; 
    self.nonRotatingViewController = nonRotating; 
    [nonRotating release]; 

    [self.window addSubview:self.rotatingViewController.view]; 
    [self.window insertSubview:self.nonRotatingViewController.view belowSubview:self.rotatingViewController.view]; 

    [self.window makeKeyAndVisible]; 

    return YES; 
} 

Spero che questo aiuti.

+0

Questo approccio non sembra funzionare per me. Ho configurato una gerarchia di viste con un controller di visualizzazione padre che contiene due controller di visualizzazione figlio (portraitViewController e rotateViewController), ciascuno contenente un UIImageView con shouldAutorotateToInterfaceOrientation che restituisce NO per portrait e YES per la rotazione. Se imposto shouldAutorotateToInterfaceOrientation su NO nel mio genitore, l'orientamento è statico, come previsto. L'impostazione shouldAutorotateToInterfaceOrientation su SÌ nel mio genitore, tuttavia, causa la rotazione di entrambi. Qualche possibilità di pubblicare un codice? Ho sbagliato da qualche parte. –

+1

@ Jason: ho aggiunto qualche codice di esempio come richiesto. – Stuart

+1

@Jason: View I controller possono essere piuttosto delicati e particolari: l'utilizzo di un controller personalizzato "contenitore" come descritto potrebbe essere ciò che sta causando il problema. Mi assicuro che le viste del controller di visualizzazione figlio siano entrambe collegate direttamente alla finestra dell'applicazione. – Stuart

10

Ecco un altro modo. Basta inserire questo codice nel vostro viewController viewDidLoad:

YourAppDelegate *delegate = [[UIApplication sharedApplication] delegate]; 
     // the view you don't want rotated (there could be a heierarchy of views here): 
    UIView *nonRotatingView = [[UIView alloc] initWithFrame:CGRectMake(100,0,30,500)]; 
    nonRotatingView.backgroundColor = [UIColor purpleColor]; 

     // make sure self.view and its children are transparent so you can see through to this view that will not be rotated behind self.view. 

    [delegate.window insertSubview:nonRotatingView belowSubview:self.view]; 

     // you can't put it in the front using: 
     // [delegate.window insertSubview:nonRotatingView aboveSubview:self.view]; 
     // It will show up the same as before 

    // you should declare nonRotatingView as a property so you can easily access it for removal, etc. 
+0

Questo funziona perfettamente per me! Più semplice dell'altra soluzione. –

+1

Qualcuno deve spiegare perché questo funziona perché il codice non lo rende ovvio –

+2

Il nonRotatingView viene inserito tra la finestra dell'app e ViewControllers e non è gestito dal viewcontroller. Il viewcontroller gestirà solo le modifiche di orientamento per le sue viste e ignorerà questa vista che è stata posta tra questa e la finestra. – mahboudz

4

mio approccio per risolvere questo problema sta facendo una contromisura utilizzando UINotification per rilevare la rotazione automatica e rotazione della vista viceversa.

Trova il codice completo qui:
https://gist.github.com/ffraenz/5945301

- (void)orientationDidChangeNotificationReceived:(NSNotification *)notification 
{ 
    // find out needed compass rotation 
    // (as a countermeasure to auto rotation) 
    float rotation = 0.0; 

    if (self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) 
     rotation = M_PI; 
    else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) 
     rotation = M_PI/2.0; 
    else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight) 
     rotation = M_PI/(- 2.0); 

    // rotate compass without auto rotation animation 
    // iOS rotation animation duration is 0.3 
    // this excludes the compassView from the auto rotation 
    // (same effect as in the camera app where controls do rotate and camera viewport don't) 
    [UIView animateWithDuration:0.3 animations:^(void) { 
     self.compassView.transform = CGAffineTransformMakeRotation(rotation); 
    }]; 
} 
+0

Questo funziona bene per iOS7 ma per qualche motivo, non funziona su iOS 8 – Jan

Problemi correlati