iOS 16 unexpected rotation behaviour

Is anyone facing any issue with rotation on iOS 16? It seems to introduce some unexpected behaviour.

For example, if we're on a Master screen that supports all orientations and device is in Landscape mode, presenting/pushing another Details screen that only supports Portrait automatically brings the app to Portrait with no animations. While on iOS 15 or earlier, the app remains at Landscape and only rotates to Portrait when physical device's orientation changes.

Then if we dismiss/pop back to Master screen, the app again unexpectedly rotates from Portrait to Landscape with no animations.

Looks like a regression bug on iOS 16 part of some recent deprecations related to shouldAutorotate or UIDevice.setValue:forKey: as well as the introduction of setNeedsUpdateOfSupportedInterfaceOrientations.

Post not yet marked as solved Up vote post of ntduyet Down vote post of ntduyet
4.4k views
  • Yeah I need to prevent screen rotation while the user is recording video, but with deprecation of shouldAutorotate I don't know how to do this. setNeedsUpdateOfSupportedInterfaceOrientations doesn't seem to be found when trying to compile and supportedInterfaceOrientations is read only. Currently, I've got a bunch of iOS16 users complaining.

Add a Comment

Replies

Somewhat related, the documentation for setNeedsUpdateOfSupportedInterfaceOrientations https://developer.apple.com/documentation/uikit/uiviewcontroller/4047535-setneedsupdateofsupportedinterfa states "By default, this method animates any changes to orientation. To perform a nonanimated update, call this method from performWithoutAnimation(_:)." and when I attempt to use performWithoutAnimation, it randomly doesn't have an effect half of the time, and I see the unwanted interface orientation change animation.

I had a similar problem. Calling setViewController:animated: of UINavigationController to replace topViewController (which is landscape) with a new landscape viewController makes the viewController try to rotate to portrait first, then suddenly change to landscape. Here is the code to reproduce this bug.

#import "ViewController.h"

@interface UINavigationController (Replace)

- (void)replaceTopWithViewController:(UIViewController *)viewController
              animated:(BOOL)animated;

- (void)replaceViewControllersInRange:(NSRange)range
         withViewControllers:(NSArray<UIViewController *> *)viewControllers
               animated:(BOOL)animated;

@end

@implementation UINavigationController (Replace)

- (void)replaceViewControllersInRange:(NSRange)range
         withViewControllers:(NSArray<UIViewController *> *)viewControllers
               animated:(BOOL)animated {
  NSMutableArray *VCs = [self.viewControllers mutableCopy];
  [VCs replaceObjectsInRange:range withObjectsFromArray:viewControllers];
  [self setViewControllers:[VCs copy] animated:animated];
}

- (void)replaceTopWithViewController:(UIViewController *)viewController animated:(BOOL)animated {
  [self replaceViewControllersInRange:NSMakeRange(self.viewControllers.count - 1, 1)
          withViewControllers:@[viewController]
                animated:animated];
}

@end

@implementation NavigationController

- (BOOL)shouldAutorotate {
  return [self.topViewController shouldAutorotate];
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
  return [self.topViewController supportedInterfaceOrientations];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
  return [self.topViewController preferredInterfaceOrientationForPresentation];
}

@end


@interface ThirdViewController : UIViewController

@end

@implementation ThirdViewController

- (BOOL)shouldAutorotate {
  return YES;
}

- (void)viewDidLoad {
  [super viewDidLoad];

  self.view.backgroundColor = UIColor.redColor;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
  return UIInterfaceOrientationMaskLandscapeRight;
}

@end

@interface SecondViewController : UIViewController

@end

@implementation SecondViewController

- (BOOL)shouldAutorotate {
  return YES;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
  return UIInterfaceOrientationMaskLandscapeRight;
}

- (void)viewDidAppear:(BOOL)animated {
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    ThirdViewController *controller = [[ThirdViewController alloc] init];
    [self.navigationController replaceTopWithViewController:controller animated:YES];
  });
}

@end

@interface ViewController ()

@end

@implementation ViewController

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
  return UIInterfaceOrientationMaskPortrait;
}

- (void)viewDidLoad {
  [super viewDidLoad];

  self.view.backgroundColor = UIColor.whiteColor;
}

- (void)viewDidAppear:(BOOL)animated {
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    SecondViewController *controller = [[SecondViewController alloc] init];
    [self.navigationController pushViewController:controller animated:YES];
  });
}

@end


I am facing some similar issues with my app too.

https://developer.apple.com/forums/thread/715052#715052021

I fixed my version of this issue by updating the supportedInterfaceOrientations and then calling self.setNeedsUpdateOfSupportedInterfaceOrientations() wrapped in an iOS version check. My VC now behaves the same as before iOS 16 update.

  // MARK: START Orientation Logic
  // For iOS 16+ devices need to update `supportedInterfaceOrientations` value for
  // supported orientations and then call `setNeedsUpdateOfSupportedInterfaceOrientations()`
  // before app will recognize the new orientation change
  var scanModeEnabled = false {
    didSet {
      if #available(iOS 16.0, *) {
        self.setNeedsUpdateOfSupportedInterfaceOrientations()
      }
    }
  }
   
  override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
    if scanModeEnabled {
      return .all
    } else {
      return .portrait
    }
  }

 // This is deprecated on iOS 16 and higher
 override var shouldAutorotate: Bool {
    return true
  }
  // MARK: END Orientation Logic
  • Hi @Morssel_K. I believe that the way you solved it, I can solve it too. Under iOS 16, supportedInterfaceOrientations would have been automatically set by shouldAutorotate, but I think that setNeedsUpdateOfSupportedInterfaceOrientations() must be called explicitly as shouldAutorotate is deprecated. But I'm curious, when did you toggle the scanModeEnabled value? thanks.

Add a Comment

Yes i am using iphone 8. Though its a 4 year old model but still working great and getting ios 16 update. But after ios 16 there is a specific rotation bug with Antutu appoication. I enabled disabled the auto rotation but the app is running with portrait mode and then close in the middle of benchmark test. Dont know how to resolve it