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让设备的旋转在触发前先稳定..

希望对你有帮助..