2012-06-18 12 views
7

La mia applicazione supporta tutti gli orientamenti tranne PortraitUpsideDown. Nella gerarchia di visualizzazione, ho un AVCaptureVideoPreviewLayer come sottolivello nella vista superiore che è UIImageView. Quindi sotto la gerarchia delle viste ci sono diverse viste di sovrapposizione che mostrano i controlli.Come gestire l'autorotazione in AVCaptureVideoPreviewLayer?

Le visualizzazioni di sovrapposizione funzionano correttamente con le modifiche di orientamento, ma non so come stare con AVCaptureVideoPreviewLayer. Voglio che si comporti come nell'app Fotocamera, in modo che previewLayer rimanga fermo e i controlli vengano riorganizzati senza problemi. In questo momento, dato che la vista principale viene ruotata sul cambio di orientamento, anche il mio livello di anteprima viene ruotato, il che significa che in visualizzazione orizzontale rimane nella visualizzazione verticale, prendendo solo una parte dello schermo e l'immagine della fotocamera ruotata di 90 gradi. Sono riuscito a ruotare manualmente il livello di anteprima, ma poi ha questa animazione di modifica dell'orientamento, che fa sì che lo sfondo venga visto per un po 'durante l'animazione.

Quindi qual è il modo corretto per eseguire l'autorotazione dei controlli mentre si effettua l'anteprima di LAYER?

+1

avuto problemi simile. Una soluzione _obvious_ non è purtroppo disponibile: speravo di osservare (con KVO) 'transform' del' presentationLayer' del layer della vista senza fortuna - l'osservazione del valore-chiave non è disponibile durante il runtime dell'animazione. Finito usando (probabilmente) la stessa soluzione di te: incorporando il livello di anteprima in un 'UIView' e ruotandolo manualmente. –

+2

Sì, ho anche finito con la rotazione manuale della vista contenente l'anteprimaLayer. Risultò essere non molto complicato. Applico CGAffineTransformMakeRotation() e quindi cambio il frame alla dimensione corretta. Ma ora ho esattamente il comportamento che voglio. – BartoNaz

+0

@BartoNaz Sono in difficoltà da un paio di giorni e tutto ciò che provo non mi dà l'effetto di camera.app in cui AVCaptureVideoPreviewLayer rimane bloccato su una vista e non esegue la rotazione animata. Potresti gentilmente fornire lo snippet di codice funzionante come risposta a questa domanda? Grazie. –

risposta

5

Nella mia implementazione ho sottoclassi l'UIView per la mia vista che voglio ruotare e qualcosa come viewController per questa vista che è solo una sottoclasse di NSObject.

In questo viewController eseguo tutti i controlli relativi ai cambi di orientamento, prendo la decisione se dovrei cambiare l'orientamento della mia vista obiettivo, e in caso affermativo, chiamo il metodo della mia vista per cambiarne l'orientamento.

Prima di tutto è necessario correggere l'orientamento dell'intera interfaccia dell'applicazione in modalità Ritratto, in modo che ACCAPtureVideoPreviewLayer rimanga sempre fermo. Questo viene fatto nel MainViewController.h:

(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation` 
{ 
    return interfaceOrientation==UIInterfaceOrientationPortrait; 
} 

E non ha prodotto alcun a tutti gli orientamenti ad eccezione del ritratto.

Per nostra abitudine viewController in grado di monitorare i cambiamenti di orientamento del dispositivo di cui abbiamo bisogno per renderlo un osservatore di notifiche corrispondenti:

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

Ho messo queste righe nel metodo della mia viewController (void)awakeFromNib. Pertanto, ogni volta che viene modificato l'orientamento del dispositivo, viene chiamato il metodo viewController orientationChanged.

Il suo scopo è verificare quale è il nuovo orientamento del dispositivo, quale era l'ultimo orientamento del dispositivo e decidere se modificarlo. Ecco l'attuazione:

if(UIInterfaceOrientationPortraitUpsideDown==[UIDevice currentDevice].orientation || 
    lastOrientation==(UIInterfaceOrientation)[UIDevice currentDevice].orientation) 
    return; 
    [[UIApplication sharedApplication]setStatusBarOrientation:[UIDevice currentDevice].orientation animated:NO]; 

    lastOrientation=[UIApplication sharedApplication].statusBarOrientation; 
    [resultView orientationChanged]; 

Se l'orientamento è lo stesso di prima o in PortraitUpsideDown poi non fare nulla. Altrimenti imposta l'orientamento della barra di stato su quello corretto, in modo che quando c'è una chiamata in entrata o ossificazione, apparirà sul lato corretto dello schermo. E poi chiamo anche il metodo nella vista di destinazione dove sono eseguite tutte le modifiche corrispondenti per il nuovo orientamento, come la rotazione, il ridimensionamento, lo spostamento degli altri elementi dell'interfaccia in questa vista corrispondente al nuovo orientamento.

Ecco l'attuazione del orientationChanged in vista di destinazione:

Float32 angle=0.f; 
UIInterfaceOrientation orientation=[UIApplication sharedApplication].statusBarOrientation; 

switch (orientation) { 
    case UIInterfaceOrientationLandscapeLeft: 
     angle=-90.f*M_PI/180.f; 
     break; 
    case UIInterfaceOrientationLandscapeRight: 
      angle=90.f*M_PI/180.f; 
     break; 
    default: angle=0.f; 
     break; 
} 
if(angle==0 && CGAffineTransformIsIdentity(self.transform)) return; 

CGAffineTransform transform=CGAffineTransformMakeRotation(angle); 

[UIView beginAnimations:@"rotateView" context:nil]; 
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut]; 
[UIView setAnimationDuration:0.35f]; 

self.transform=transform; 

[UIView commitAnimations]; 

Naturalmente qui è possibile aggiungere altre modifiche come la traduzione, il ridimensionamento di diversi punti di vista della vostra interfaccia che devono rispondere ai nuovi orientamenti e animare loro.

Inoltre potresti non aver bisogno di viewController per questo, ma fai tutto solo nella classe della tua vista. Spero che l'idea generale sia chiara.

Inoltre, non dimenticare di fermare la notifica di ottenere per l'orientamento cambia quando non è necessario come loro:

[[NSNotificationCenter defaultCenter]removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; 
[[UIDevice currentDevice]endGeneratingDeviceOrientationNotifications]; 
0

il problema principale è che quando ricevo la notifica, la barra di stato non è ancora ruotato, quindi il controllo del valore corrente fornisce di fatto il valore prima della rotazione. Così ho aggiunto un po 'di ritardo (qui 2 secondi) prima di chiamare il metodo che controlla la statusbarorientation e ruota la mia visualizzazione secondaria:

-(void) handleNotification:(NSNotification *) notification 
{  
    (void) [NSTimer scheduledTimerWithTimeInterval:(2.0) 
              target:self 
              selector:@selector(orientationChanged) 
              userInfo:nil 
              repeats:NO ] ; 
} 

Il resto del codice è quello BartoNaz.

+0

Non sono sicuro se questo è corretto. Questa è esattamente l'idea di questo metodo, che la barra di stato non ruota da sola. E tu non controlli l'orientamento corrente della barra di stato. Stai controllando l'orientamento del dispositivo e confrontalo con l'orientamento dell'ultima volta in cui è stato chiamato questo metodo. E quando il tuo metodo decide di cambiare l'orientamento, lo fa manualmente, chiamando '[[UIApplication sharedApplication] setStatusBarOrientation: [UIDevice currentDevice] .orientation animated: NO];' – BartoNaz

+0

Dovresti utilizzare l'orientamento del dispositivo invece dell'orientamento della barra di stato qui . –

1

iOS 8 soluzione:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { 
    if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft) { 
     self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft; 
    } else { 
     self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; 
    } 
} 

nel codice di installazione:

self.layer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session]; 
self.layer.videoGravity = AVLayerVideoGravityResizeAspectFill; 
if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft) { 
    self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft; 
} else { 
    self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; 
}