La orientación de CAlayer de iOS AVCaptureVideoPreviewLayer no gira

introduzca la descripción de la imagen aquí Resumen: No puedo obligar a CALayer a responder correctamente a los cambios de orientación. Cada vez que trato de usar cgaffinetransform obtengo resultados extraños (la capa no está cinput). ¡Cualquier ayuda será apreciada! Gracias,

Proceso Agrego una vista previa de video usando la subclass AVCaptureVideoPreviewLayer. Cuando el dispositivo está en una orientación vertical todo se ve bien. El problema aparece cuando el dispositivo gira a la orientación horizontal (izquierda o derecha) o al revés.

Estoy agregando una vista previa del video usando la subclass AVCaptureVideoPreviewLayer. Cuando el dispositivo está en una orientación vertical todo se ve bien. El problema aparece cuando el dispositivo gira a la orientación horizontal (izquierda o derecha) o al revés.

Estoy agregando una capa de vista previa con el siguiente código:

CGRect layerRect = [[[self view] layer] bounds]; [[[self captureManager] previewLayer] setBounds:layerRect]; [[[self captureManager] previewLayer]setFrame:CGRectMake(0, height, width, height)]; [[[self captureManager] previewLayer] setPosition:CGPointMake(CGRectGetMidX(layerRect),CGRectGetMidY(layerRect))]; 

Y se muestra correctamente en el modo vertical. Cuando trato de rotar el dispositivo, la capa de vista previa se comporta de manera extraña. Parece que no cambia de tamaño, y no gira correctamente.

Traté de solucionarlo agregando el siguiente método

 -(void)rotateLayer{ CALayer * stuckview = [[self captureManager] previewLayer]; CGRect layerRect = [[[self view] layer] bounds]; UIDeviceOrientation orientation =[[UIDevice currentDevice]orientation]; switch (orientation) { case UIDeviceOrientationLandscapeLeft: stuckview.affineTransform = CGAffineTransformMakeRotation(M_PI+ M_PI_2); // 270 degress NSLog(@"Landscape Left"); [stuckview setPosition: CGPointMake(self.view.bounds.size.width /2.0, self.view.bounds.size.height /2.0)]; break; case UIDeviceOrientationLandscapeRight: stuckview.affineTransform = CGAffineTransformMakeRotation(M_PI_2); // 90 degrees NSLog(@"Landscape Right"); [stuckview setPosition: CGPointMake(self.view.bounds.size.width /2.0, self.view.bounds.size.height /2.0)]; break; case UIDeviceOrientationPortraitUpsideDown: stuckview.affineTransform = CGAffineTransformMakeRotation(M_PI); // 180 degrees NSLog(@"Landscape Upside down"); break; default: stuckview.affineTransform = CGAffineTransformMakeRotation(0.0); break; } float h1 = stuckview.frame.size.height; float w1 = stuckview.frame.size.width; if(UIDeviceOrientationIsPortrait(orientation)) { stuckview.position =CGPointMake(h1/2.0, w1/2.0); NSLog(@"Portrait"); } else{ stuckview.position =CGPointMake(w1/2.0, h1/2.0); NSLog(@"Portrait"); } 

}

Después de agregar el método anterior puedo ver un progreso. Ahora la capa gira correctamente reflejando la orientación actual del dispositivo y se muestra correctamente en modo horizontal, pero NO en modo vertical.

La capa no está colocada correctamente, no está cinput en la pantalla (mira la captura de pantalla). Para ver lo que sucede, agregué siguientes declaraciones de debugging:

 CALayer * stuckview = [[self captureManager] previewLayer]; CGRect layerRect = [[[self view] layer] bounds]; float h = stuckview.bounds.size.height; float w = stuckview.bounds.size.width; float x = stuckview.bounds.origin.x; float y = stuckview.bounds.origin.y; float h1 = stuckview.frame.size.height; float w1 = stuckview.frame.size.width; float x1 = stuckview.frame.origin.x; float y1 = stuckview.frame.origin.y; NSLog(@"%f %f %f %f ", h,w,x,y ); NSLog(@"%f %f %f %f ", h1,w1,x1,y1 ); NSLog(@"Anchor Point: %f %f",stuckview.anchorPoint.x, stuckview.anchorPoint.y); NSLog(@"Position: %f %f",stuckview.position.x, stuckview.position.y); CGAffineTransform at = stuckview.affineTransform; NSLog(@"Affine Transform After : %f %f %f %f %f %f %f", at.a,at.b, at.c, at.d, at.tx,at.tx, at.ty); 

Y obtenga el siguiente resultado:

 2012-09-30 13:25:12.067 RotatePreviewLayer[2776:907] 1024.000000 768.000000 0.000000 0.000000 

2012-09-30 RotatePreviewLayer [2776: 907] 1024.000000 768.000000 128.000000 -128.000000

 2012-09-30 13:25:12.070 RotatePreviewLayer[2776:907] Portrait 2012-09-30 13:25:12.072 RotatePreviewLayer[2776:907] Anchor Point: 0.500000 0.500000 2012-09-30 13:25:12.074 RotatePreviewLayer[2776:907] Position: 512.000000 384.000000 2012-09-30 13:25:12.076 RotatePreviewLayer[2776:907] Affine Transform after: -1.000000 0.000000 -0.000000 -1.000000 0.000000 0.000000 0.000000 

Observe la segunda línea de la salida de debugging. El marco de la capa de vista previa se mueve por 128, -128. ¿Alguien me puede explicar por qué sucede esto y cómo solucionar los problemas de orientación con la capa de vista previa? gracias Janusz

¿Qué hay de esto?

 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { _videoPreviewLayer.connection.videoOrientation = toInterfaceOrientation; } 

Todavía no estoy seguro de qué está causando el problema pero logré solucionarlo. Así es como lo hice:

en viewDidLoad estoy agregando una capa:

 CGRect layerRect = [[[self view] layer] bounds]; [[[self captureManager] previewLayer] setBounds:layerRect]; [[[self captureManager] previewLayer] setPosition:CGPointMake(CGRectGetMidX(layerRect),CGRectGetMidY(layerRect))]; [[[self view] layer] addSublayer:[[self captureManager] previewLayer]]; 

Luego estoy agregando la llamada al método rotateLayer para hacer Rotar

 - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{ [self rotateLayer]; } 

y finalmente el método rotateLayer se ve así:

 -(void)rotateLayer{ CALayer * stuckview = [[self captureManager] previewLayer]; CGRect layerRect = [[[self view] layer] bounds]; UIDeviceOrientation orientation =[[UIDevice currentDevice]orientation]; switch (orientation) { case UIDeviceOrientationLandscapeLeft: stuckview.affineTransform = CGAffineTransformMakeRotation(M_PI+ M_PI_2); // 270 degress break; case UIDeviceOrientationLandscapeRight: stuckview.affineTransform = CGAffineTransformMakeRotation(M_PI_2); // 90 degrees break; case UIDeviceOrientationPortraitUpsideDown: stuckview.affineTransform = CGAffineTransformMakeRotation(M_PI); // 180 degrees break; default: stuckview.affineTransform = CGAffineTransformMakeRotation(0.0); [stuckview setBounds:layerRect]; break; } [stuckview setPosition:CGPointMake(CGRectGetMidX(layerRect),CGRectGetMidY(layerRect))]; } 

Todavía no entiendo por qué funciona de esta manera. Si alguien puede explicarlo, será genial.

Este es el método que uso en el controller de vista para mantener la orientación de la capa de captura de modo que siempre esté hacia la derecha:

 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration if (_previewLayer.connection.supportsVideoOrientation) { switch (toInterfaceOrientation) { case UIInterfaceOrientationPortrait: { _previewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortrait; break; } case UIInterfaceOrientationPortraitUpsideDown: { _previewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortraitUpsideDown; break; } case UIInterfaceOrientationLandscapeLeft: { _previewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft; break; } case UIInterfaceOrientationLandscapeRight: { _previewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; break; } } } 

Un poco cursi, pero a salvo incluso si alguna enumeración cambia alguna vez en el futuro.

@Janusz Chudzynski aquí está la explicación detallada de qué rotateLayer método rotateLayer

Este método se crea después de examinar cómo la orientación diferente afecta al previewLayer para que el creador se haya comprobado cuando la orientación está en LandscapeLeft entonces debería girar 270 grados para que esté en la position correcta para el que ha utilizado

 stuckview.affineTransform = CGAffineTransformMakeRotation(M_PI+ M_PI_2); M_PI 3.14159265358979323846264338327950288 // pi = 180 degree + M_PI_2 1.57079632679489661923132169163975144 // pi/2 = 90 degree // Total = 270 degree 

así que creater ha notado que si voy a rotar previewLayer a 270 grados cuando está en LandscapeLeft entonces estará en la position correcta justo como si hubiera previewLayer por cada rotation posible

 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { previewLayer.orientation = toInterfaceOrientation; } 

Estoy con @Siegfault, aunque también encontré que cuando mi vista se carga en orientación horizontal en el iPad inicialmente, la orientación sigue siendo correcta. Para corregir, viewDidAppear: mismo método de delegado en viewDidAppear: con la interfaceOrientation viewDidAppear: :

 - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self willRotateToInterfaceOrientation:self.interfaceOrientation duration:0.0]; } 

La respuesta usando willRotateToUserInterfaceOrientation funciona bien, excepto que ese método ha quedado en desuso. Entonces, si puedes usar iOS 9, esta es la manera de hacerlo, en Swift:

 override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) { let newOrientation = UIDevice.currentDevice().orientation switch newOrientation { case .LandscapeLeft: self.capturePreviewLayer?.connection.videoOrientation = .LandscapeLeft case .LandscapeRight: self.capturePreviewLayer?.connection.videoOrientation = .LandscapeRight case .Portrait, .Unknown, .FaceUp, .FaceDown: self.capturePreviewLayer?.connection.videoOrientation = .Portrait case .PortraitUpsideDown: self.capturePreviewLayer?.connection.videoOrientation = .PortraitUpsideDown } super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator) } 
 // layout iOS 8+ animated - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) { NSString *timingFunc = nil; switch ( [context completionCurve] ) { case UIViewAnimationCurveEaseIn: timingFunc = kCAMediaTimingFunctionEaseIn; break; case UIViewAnimationCurveEaseInOut: timingFunc = kCAMediaTimingFunctionEaseInEaseOut; break; case UIViewAnimationCurveEaseOut: timingFunc = kCAMediaTimingFunctionEaseOut; break; case UIViewAnimationCurveLinear: timingFunc = kCAMediaTimingFunctionLinear; break; } [CATransaction begin]; [CATransaction setAnimationDuration:[context transitionDuration]]; [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:timingFunc]]; [self updatePreviewLayer]; [CATransaction commit]; UIInterfaceOrientation toOrientation = [[UIApplication shanetworkingApplication] statusBarOrientation]; // layout ui if needed } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) { }]; [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; } - (void)updatePreviewLayer { CGAffineTransform transform = CGAffineTransformIdentity; switch ( UIDevice.currentDevice.orientation ) { case UIDeviceOrientationLandscapeLeft: transform = CGAffineTransformRotate(transform, -M_PI_2); break; case UIDeviceOrientationLandscapeRight: transform = CGAffineTransformRotate(transform, M_PI_2); break; case UIDeviceOrientationPortraitUpsideDown: transform = CGAffineTransformRotate(transform, M_PI); break; default: break; } preview.affineTransform = transform; preview.frame = self.view.bounds; }