Быстрый поворот устройства iOS в 180 раз приводит к просмотру камеры в обратном порядке

Я применил приведенный ниже код, чтобы изменить ориентацию AVCaptureVideoSession на основе UIInterfaceOrientation:

- (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 градусов, камера будет отображаться с ног на голову.

Вот что выглядит камера перед вращением:

enter image description here

И вот как это выглядит после вращения:

enter image description here

Кроме того, вот моя реализация 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-градусное вращение происходит?

Ответ 1

У меня была такая же проблема перед тем, что решило мою проблему.

Я объявила глобальную переменную:

@interface YourViewController ()
{
    ...
    UIDevice *iOSDevice;
}

..

@end

В implementation (.m файл) я объявил наблюдателя NSNotification в -viewWillAppear как:

@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];

}

Я сделал функцию, которая возвращает ориентацию скопированного текущего устройства, например:

Возьмите добычу по моему методу -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;
}

Edit

Для ориентации по умолчанию: вам нужно проверить "текущую" ориентацию.

- (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, разрешите вращение устройства сначала перед запуском.

Надеюсь, это поможет вам.

Ответ 2

Я считаю, что это происходит, потому что viewDidLayoutSubviews не вызывается, когда представление изменяется с ориентации Portrait на UpsideDown. Когда вы вращаетесь очень быстро, он идет от одного к другому прямо. Если вы медленно вращаетесь, он проходит через ландшафтную ориентацию, и поэтому viewDidLayoutSubviews вызывается, а затем ваш метод.

Я советую вам использовать один из методов, вызываемых при изменении ориентации. Я знаю, что Apple советует использовать их все меньше и меньше, потому что они хотят, чтобы представления менялись при изменении размера, но есть случаи, подобные этому, когда вам действительно нужно знать, что ориентация изменилась.

Например, вы можете зарегистрироваться для получения этих уведомлений: UIApplicationDidChangeStatusBarOrientationNotification