Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

didRotateFromInterfaceOrientation is deprecated in iOS 8

Since iOS 8.0, the function didRotateFromInterfaceOrientation becomes deprecated and I now have no idea how to change the orientation of AVCaptureVideoPreviewLayer properly.

According to "interfaceOrientation" is deprecated in iOS 8, How to change this method Objective C, I have the following working code to get the AVCaptureVideoOrientation based on statusBarOrientation:

- (AVCaptureVideoOrientation)getOrientation {
    // Translate the orientation
    switch ([[UIApplication sharedApplication] statusBarOrientation]) {
        case UIInterfaceOrientationPortrait:
            return AVCaptureVideoOrientationPortrait;
        case UIInterfaceOrientationPortraitUpsideDown:
            return AVCaptureVideoOrientationPortraitUpsideDown;
        case UIInterfaceOrientationLandscapeLeft:
            return AVCaptureVideoOrientationLandscapeLeft;
        case UIInterfaceOrientationLandscapeRight:
            return AVCaptureVideoOrientationLandscapeRight;
        default:
            return AVCaptureVideoOrientationPortrait;
    }
}

I need to call this following function when the orientation is changed:

[self.videoPreviewLayer.connection setVideoOrientation:[self getOrientation]];

But I don't know where is the right place to put it.

Attempt 1

Since didRotateFromInterfaceOrientation is deprecated and Apple suggest to use viewWillTransitionToSize:withTransitionCoordinator:, this seems natural to try:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
    [coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        [self.videoPreviewLayer.connection setVideoOrientation:[self getOrientation]];
    }];
}

But this function doesn't get called if the UIViewController is being presented (seems to be a known bug based on "viewWillTransitionToSize:" not called in iOS 9 when the view controller is presented modally). Even if this bug is resolved, there is another problem: orientation change is not equivalent to size change. For instance, if a view is presented with UIModalPresentationFormSheet as the following,

SecondViewController *vc = [[SecondViewController alloc] init];
vc.view.backgroundColor = [UIColor greenColor];
vc.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:vc animated:true completion:nil];

the presented view size the same under landscape and portrait mode for iPad:

Therefore, I cannot simply use viewWillTransitionToSize.

Attempt 2

I notice that UIDeviceOrientationDidChangeNotification is not deprecated. Great! I tried register a notification with that:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    // Some other code to set up the preview layer 
    // ...

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

- (void)viewWillDisappear:(BOOL)animated
{
    [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}

- (void)deviceOrientationDidChange:(NSNotification *)notification {
    [self.videoPreviewLayer.connection setVideoOrientation:[self getOrientation]];
}

This is almost working and it is so closed! The problem is that when I get the device orientation changed notification, the animation of rotating the view maybe started and maybe not. Or in another word, the return value [[UIApplication sharedApplication] statusBarOrientation] may be incorrect if the device is already rotated up side down but the animation is still pending.

And I don't know what else to try :( Any ideas?

like image 501
Yuchen Avatar asked Sep 25 '22 16:09

Yuchen


1 Answers

The following should be able to handle orientation of video preview properly in swift 2.1:

override func viewWillAppear(animated: Bool) {
    super.viewDidLoad()

    NSNotificationCenter.defaultCenter().addObserver(self,
        selector: Selector("deviceOrientationDidChange:"),
        name: UIDeviceOrientationDidChangeNotification,
        object: nil)
}

override func viewDidDisappear(animated: Bool) {
    NSNotificationCenter.defaultCenter().removeObserver(self)
    super.viewDidDisappear(animated)
}

func deviceOrientationDidChange(sender: AnyObject) {
    let deviceOrientation = UIDevice.currentDevice().orientation;
    switch (deviceOrientation)
    {
    case .Portrait:
       previewLayer.connection.videoOrientation = .Portrait
    case .LandscapeLeft:
       previewLayer.connection.videoOrientation = .LandscapeRight
    case .LandscapeRight:
        previewLayer.connection.videoOrientation = .LandscapeLeft
    case .PortraitUpsideDown:
       previewLayer.connection.videoOrientation = .PortraitUpsideDown
    default:
        break
    }
}
like image 191
Yuchen Avatar answered Sep 29 '22 05:09

Yuchen