¿Cómo manejar la autorotation en AVCaptureVideoPreviewLayer?

Mi aplicación admite todas las orientaciones, excepto PortraitUpsideDown. En mi jerarquía de vista, tengo una AVCaptureVideoPreviewLayer como subcapa en la vista superior que es UIImageView. A continuación, en la jerarquía de la vista, hay varias vistas superpuestas que muestran los controles.

Las vistas superpuestas funcionan correctamente con los cambios de orientación, pero no sé cómo estar con este AVCaptureVideoPreviewLayer. Quiero que se comporte como en la aplicación Camera, de modo que previewLayer se queda quieta y los controles se reorganizan sin problemas. En este momento, dado que la vista principal gira sobre el cambio de orientación, mi capa de vista previa también gira, lo que significa que en la vista horizontal permanece en la vista vertical, tomando solo una parte de la pantalla y girando la image de la camera 90 grados. He logrado rotar manualmente la capa de vista previa, pero luego tiene esta animation de cambio de orientación, lo que lleva a que se vea el background durante un time durante la animation.

Entonces, ¿cuál es la forma correcta de autorotizar los controles mientras se hace la vista previa. ¿Manténgase quieto?

En mi implementación, subclasé la UIView para mi vista, que quiero rotar y algo así como viewController para esta vista, que es solo una subclass de NSObject.

En este viewController, hago todas las comprobaciones relacionadas con los cambios de orientación, tomo una decisión si debo cambiar la orientación de mi vista de destino y, si es así, llamo al método de mi vista para cambiar su orientación.

En primer lugar, debemos corregir la orientación de la interfaz de la aplicación completa en el modo vertical, de modo que nuestro ACCaptureVideoPreviewLayer siempre permanezca inmobile. Esto se hace en MainViewController.h :

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

Devuelve NO a todas las orientaciones excepto a Portrait.

Para que nuestro custom viewController pueda rastrear los cambios de orientación del dispositivo, debemos hacerle un observador de las notifications correspondientes:

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

Puse estas líneas en el método (void)awakeFromNib de mi viewController. Entonces, cada vez que se cambia la orientación del dispositivo, se llamará al método viewController's orientationChanged .

Su propósito es verificar cuál es la nueva orientación del dispositivo, cuál fue la última orientación del dispositivo y decidir si cambiarlo. Aquí está la implementación:

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

Si la orientación es la misma que la anterior o en PortraitUpsideDown, no haga nada. De lo contrario, establece la orientación de la barra de estado en la correcta, de modo que cuando haya una llamada entrante o una osificación, aparecerá en el lado correcto de la pantalla. Y luego llamo también a método en la vista de destino donde se realizan todos los cambios correspondientes para la nueva orientación, como rotar, cambiar el tamaño, mover los otros elementos de la interfaz en esta vista correspondiente a la nueva orientación.

Aquí está la implementación de la orientationChanged en la vista de destino:

 Float32 angle=0.f; UIInterfaceOrientation orientation=[UIApplication shanetworkingApplication].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]; 

Por supuesto, aquí puede agregar otros cambios, como la traducción, la escala de diferentes vistas de su interfaz que necesitan responder a una nueva orientación y animarlas.

Además, es posible que no necesite viewController para esto, pero simplemente haga todo en la class de su vista. Espero que la idea general sea clara.

Además, no olvide dejar de recibir notifications de cambios de orientación cuando no los necesite como:

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

solución iOS 8:

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

en su código de configuration:

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

El problema principal es que cuando recibo la notificación, la barra de estado aún no ha girado, por lo que al comprobar el valor actual se obtiene el valor antes de la rotation. Entonces agregué un pequeño retraso (aquí 2 segundos) antes de llamar al método que verifica la orientación de estado y gira mi subvista:

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

El rest del código es el de BartoNaz.