iOS 设备快速旋转 180 度会导致摄像头上下颠倒
Quick 180 rotation of iOS Device results in the camera viewing upside-down
我已经实现了下面的代码来根据 UIInterfaceOrientation
:
改变 AVCaptureVideoSession
的方向
- (AVCaptureVideoOrientation)interfaceOrientationToVideoOrientation:(UIInterfaceOrientation)orientation {
switch (orientation) {
case UIInterfaceOrientationPortrait:
return AVCaptureVideoOrientationPortrait;
case UIInterfaceOrientationPortraitUpsideDown:
return AVCaptureVideoOrientationPortraitUpsideDown;
case UIInterfaceOrientationLandscapeLeft:
return AVCaptureVideoOrientationLandscapeLeft;
case UIInterfaceOrientationLandscapeRight:
return AVCaptureVideoOrientationLandscapeRight;
default:
break;
}
NSLog(@"Warning - Didn't recognise interface orientation (%ld)",(long)orientation);
return AVCaptureVideoOrientationPortrait;
}
这段代码几乎可以完美运行。但是,出现的一个问题是,如果您快速将 iOS 设备旋转 180 度,相机将倒置显示。
这是旋转前的相机视图:
这是旋转后的样子:
此外,这是我对 viewDidLayoutSubviews
的实现:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
previewLayer.frame = self.view.bounds;
self.view.translatesAutoresizingMaskIntoConstraints = NO;
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.view.layer addSublayer:previewLayer];
if (previewLayer.connection.supportsVideoOrientation) {
previewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation:[UIApplication sharedApplication].statusBarOrientation];
}
}
有谁知道为什么在发生这种 180 度旋转时相机视图会颠倒?
我相信这是因为 viewDidLayoutSubviews
在视图从纵向方向更改为颠倒方向时不会被调用。当你旋转得非常快时,它会从一个直线移动到另一个直线。如果您缓慢旋转,它会穿过横向方向,因此 viewDidLayoutSubviews
会被调用,随后会调用您的方法。
我建议您使用其中一种在方向改变时调用的方法。我知道 Apple 建议越来越少地使用它们,因为他们希望视图在尺寸改变时改变,但也有一些情况,比如这个,当你真的需要知道方向改变时。
例如,您可以注册接收此通知:
UIApplicationDidChangeStatusBarOrientationNotification
我之前遇到过同样的问题,解决了我的问题。
我声明了一个全局变量:
@interface YourViewController ()
{
...
UIDevice *iOSDevice;
}
..
@end
在 implementation
(.m 文件)下,我在 -viewWillAppear
下声明了 NSNotification 观察者,例如:
@implementation YourViewController
-(void)viewWillAppear:(BOOL)animated
{
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:[UIDevice currentDevice]];
}
-(void)viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}
- (void)orientationChanged:(NSNotification *)notification
{
iOSDevice = notification.object;
previewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation];
//or
[self setAutoVideoConnectionOrientation:YES];
}
我做了一个函数,returns 复制当前设备的方向,如:
利用我的 -interfaceOrientationToVideoOrientation
方法,它将 iOSDevice.orientation
转换为 AVCaptureVideoOrientation
与你那里的相同..
(在我的例子中,我需要下面的这个功能,但出于某种原因看一下它可能对你有用)
- (AVCaptureVideoOrientation)interfaceOrientationToVideoOrientation
{
switch (iOSDevice.orientation) {
case UIInterfaceOrientationPortraitUpsideDown:
return AVCaptureVideoOrientationPortraitUpsideDown;
case UIInterfaceOrientationLandscapeLeft:
return AVCaptureVideoOrientationLandscapeLeft;
case UIInterfaceOrientationLandscapeRight:
return AVCaptureVideoOrientationLandscapeRight;
default:
case UIDeviceOrientationPortrait:
return AVCaptureVideoOrientationPortrait;
}
}
- (AVCaptureConnection *)setAutoVideoConnectionOrientation:(BOOL)status
{
AVCaptureConnection *videoConnection = nil;
//This is for me
//for (AVCaptureConnection *connection in stillImageOutput.connections) {
//This might be for you
for (AVCaptureConnection *connection in previewLayer.connection) {
for (AVCaptureInputPort *port in [connection inputPorts])
{
if ([[port mediaType] isEqual:AVMediaTypeVideo])
{
videoConnection = connection;
if (status == YES)
{
[videoConnection setVideoOrientation:[self interfaceOrientationToVideoOrientation]];
}
else
{
[videoConnection setVideoOrientation:AVCaptureVideoOrientationPortrait];
}
}
if (videoConnection)
{
break;
}
}
}
return videoConnection;
}
编辑
对于默认方向:您需要检查 "current" 方向。
- (void)viewDidLoad
{
[super viewDidLoad];
// assuming you finish setting the `previewLayer`
..
// after all of that code, when the view is ready for displaying
// if you implemented this approach the best thing to do is:
// set this
iOSDevice = [UIDevice currentDevice];
// then call
previewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation];
// to update the `previewLayer.connection.videoOrientation ` for default orientation
}
注:
使用NSNotificationCenter
让设备的旋转在触发前先稳定..
希望对你有帮助..
我已经实现了下面的代码来根据 UIInterfaceOrientation
:
AVCaptureVideoSession
的方向
- (AVCaptureVideoOrientation)interfaceOrientationToVideoOrientation:(UIInterfaceOrientation)orientation {
switch (orientation) {
case UIInterfaceOrientationPortrait:
return AVCaptureVideoOrientationPortrait;
case UIInterfaceOrientationPortraitUpsideDown:
return AVCaptureVideoOrientationPortraitUpsideDown;
case UIInterfaceOrientationLandscapeLeft:
return AVCaptureVideoOrientationLandscapeLeft;
case UIInterfaceOrientationLandscapeRight:
return AVCaptureVideoOrientationLandscapeRight;
default:
break;
}
NSLog(@"Warning - Didn't recognise interface orientation (%ld)",(long)orientation);
return AVCaptureVideoOrientationPortrait;
}
这段代码几乎可以完美运行。但是,出现的一个问题是,如果您快速将 iOS 设备旋转 180 度,相机将倒置显示。
这是旋转前的相机视图:
这是旋转后的样子:
此外,这是我对 viewDidLayoutSubviews
的实现:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
previewLayer.frame = self.view.bounds;
self.view.translatesAutoresizingMaskIntoConstraints = NO;
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.view.layer addSublayer:previewLayer];
if (previewLayer.connection.supportsVideoOrientation) {
previewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation:[UIApplication sharedApplication].statusBarOrientation];
}
}
有谁知道为什么在发生这种 180 度旋转时相机视图会颠倒?
我相信这是因为 viewDidLayoutSubviews
在视图从纵向方向更改为颠倒方向时不会被调用。当你旋转得非常快时,它会从一个直线移动到另一个直线。如果您缓慢旋转,它会穿过横向方向,因此 viewDidLayoutSubviews
会被调用,随后会调用您的方法。
我建议您使用其中一种在方向改变时调用的方法。我知道 Apple 建议越来越少地使用它们,因为他们希望视图在尺寸改变时改变,但也有一些情况,比如这个,当你真的需要知道方向改变时。
例如,您可以注册接收此通知:
UIApplicationDidChangeStatusBarOrientationNotification
我之前遇到过同样的问题,解决了我的问题。
我声明了一个全局变量:
@interface YourViewController ()
{
...
UIDevice *iOSDevice;
}
..
@end
在 implementation
(.m 文件)下,我在 -viewWillAppear
下声明了 NSNotification 观察者,例如:
@implementation YourViewController
-(void)viewWillAppear:(BOOL)animated
{
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:[UIDevice currentDevice]];
}
-(void)viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}
- (void)orientationChanged:(NSNotification *)notification
{
iOSDevice = notification.object;
previewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation];
//or
[self setAutoVideoConnectionOrientation:YES];
}
我做了一个函数,returns 复制当前设备的方向,如:
利用我的 -interfaceOrientationToVideoOrientation
方法,它将 iOSDevice.orientation
转换为 AVCaptureVideoOrientation
与你那里的相同..
(在我的例子中,我需要下面的这个功能,但出于某种原因看一下它可能对你有用)
- (AVCaptureVideoOrientation)interfaceOrientationToVideoOrientation
{
switch (iOSDevice.orientation) {
case UIInterfaceOrientationPortraitUpsideDown:
return AVCaptureVideoOrientationPortraitUpsideDown;
case UIInterfaceOrientationLandscapeLeft:
return AVCaptureVideoOrientationLandscapeLeft;
case UIInterfaceOrientationLandscapeRight:
return AVCaptureVideoOrientationLandscapeRight;
default:
case UIDeviceOrientationPortrait:
return AVCaptureVideoOrientationPortrait;
}
}
- (AVCaptureConnection *)setAutoVideoConnectionOrientation:(BOOL)status
{
AVCaptureConnection *videoConnection = nil;
//This is for me
//for (AVCaptureConnection *connection in stillImageOutput.connections) {
//This might be for you
for (AVCaptureConnection *connection in previewLayer.connection) {
for (AVCaptureInputPort *port in [connection inputPorts])
{
if ([[port mediaType] isEqual:AVMediaTypeVideo])
{
videoConnection = connection;
if (status == YES)
{
[videoConnection setVideoOrientation:[self interfaceOrientationToVideoOrientation]];
}
else
{
[videoConnection setVideoOrientation:AVCaptureVideoOrientationPortrait];
}
}
if (videoConnection)
{
break;
}
}
}
return videoConnection;
}
编辑
对于默认方向:您需要检查 "current" 方向。
- (void)viewDidLoad
{
[super viewDidLoad];
// assuming you finish setting the `previewLayer`
..
// after all of that code, when the view is ready for displaying
// if you implemented this approach the best thing to do is:
// set this
iOSDevice = [UIDevice currentDevice];
// then call
previewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation];
// to update the `previewLayer.connection.videoOrientation ` for default orientation
}
注:
使用NSNotificationCenter
让设备的旋转在触发前先稳定..
希望对你有帮助..