5

I am seeing a strange issue with autorotation on iOS 16 that is not seen in other iOS versions. And worse, the issue is NOT seen if I connect device to XCode and debug. It is ONLY seen when I directly launch the app on device by touching the app icon, and that's the reason I am unable to identify any fix. What I do is I disable autorotation on app startup (by returning only .landscape in supportedInterfaceOrientations) and then very shortly after say 0.2 seconds, I force autorotation to current interface orientation of device (using self.setNeedsUpdateOfSupportedInterfaceOrientations). But autorotation fails if app is directly launched by touching the app icon (as opposed to launching from debugger)!

Here is a sample code that fully reproduces the issue. When running the app, make sure iPhone is held in portrait mode. The function viewWillTransition(to size:, with coordinator:) is not called and all the subviews are not autorotated. Please suggest me a workaround!

import UIKit

class ViewController: UIViewController {

 public var windowOrientation: UIInterfaceOrientation {
     return view.window?.windowScene?.interfaceOrientation ?? .unknown
 }

  private var disableAutoRotation = true

  override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    
     var orientations:UIInterfaceOrientationMask = .landscapeRight
    
      if !self.disableAutoRotation {
         orientations = .all
      }
   
       return orientations
    }


 override func viewDidLoad() {
     super.viewDidLoad()
    // Do any additional setup after loading the view.
    
      self.view.backgroundColor = UIColor.systemGreen
    
     DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: {
         self.autoRotateNotification()
     })
 }

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    
    super.viewWillTransition(to: size, with: coordinator)

    let orientation = windowOrientation

        
    coordinator.animate(alongsideTransition: {_ in
    
    }, completion: { [unowned self] (UIViewControllerTransitionCoordinatorContext) -> Void in
        
        let orient = self.windowOrientation
        
        if orient.isLandscape {
            self.view.backgroundColor = UIColor.systemGreen
        } else {
            self.view.backgroundColor = UIColor.systemOrange
        }
       
    })
}

func autoRotateNotification() {
  
   DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
     
       /*
        * HELP::: This code does something only when debug directly from XCode,
        * not when directly launching the app on device!!!!
        */

       self.disableAutoRotation = false

       if #available(iOS 16.0, *) {
           UIView.performWithoutAnimation {
               self.setNeedsUpdateOfSupportedInterfaceOrientations()
           }
          
       } else {
           // Fallback on earlier versions
          UIViewController.attemptRotationToDeviceOrientation()
       }
   })
}


 }

EDIT: Someone mentioned moving the autorotateNotification to viewDidAppear fixes the issue, but it does not. As already stated, there is no issue if app is launched via XCode debugger. The issue is observed only when app is directly launched by touching the app icon! Here is sample code for every one's reference that also fails.

 import UIKit

 class ViewController: UIViewController {


   public var windowOrientation: UIInterfaceOrientation {
       return view.window?.windowScene?.interfaceOrientation ?? .unknown
   }

    private var disableAutoRotation = true

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    
       var orientations:UIInterfaceOrientationMask = .landscapeRight
    
     if !self.disableAutoRotation {
         orientations = .all
     }
   
      return orientations
   }

   override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
      return .landscapeLeft
   }

   private var autoRotated = false

   override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    
    if !autoRotated {
        autoRotated = true
        
        self.autoRotateNotification()     
    }
}


override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    
    super.viewWillTransition(to: size, with: coordinator)
    
    coordinator.animate(alongsideTransition: {_ in
    
    }, completion: { [unowned self] (UIViewControllerTransitionCoordinatorContext) -> Void in
        let orient = self.windowOrientation
        
        if orient.isLandscape {
            self.view.backgroundColor = UIColor.systemGreen
        } else {
            self.view.backgroundColor = UIColor.systemOrange
        }
       
    })
}


func autoRotateNotification() {
  
    self.disableAutoRotation = false

    if #available(iOS 16.0, *) {
        self.setNeedsUpdateOfSupportedInterfaceOrientations()
    } else {
        // Fallback on earlier versions
        UIViewController.attemptRotationToDeviceOrientation()
    }
 }


 }
Deepak Sharma
  • 5,577
  • 7
  • 55
  • 131
  • experiencing similar issue with autorotation on iOS 16, calling to windowScene.requestGeometryUpdate didn't help – Gutty1 Sep 16 '22 at 08:31
  • @Gutty1 tried everything, nothing helps. It's shocking such a serious bug exists without workaround! – Deepak Sharma Sep 16 '22 at 09:45
  • I'm also tried to use windowScene.requestGeometryUpdate but getting error like the viewcontroller supporting portrait mode only. any idea about this issue please help :( – Jok3r Sep 16 '22 at 17:48
  • @Jok3r I have lost all hope, I don't think filing a DTS request would do any good as well. – Deepak Sharma Sep 16 '22 at 19:27
  • @DeepakSharma I'm also done. I have also issue in the live app. nobody really wants to fix this , not sure why :( – Jok3r Sep 17 '22 at 17:26

1 Answers1

-1

viewWillTransition seems not called because your function autoRotateNotification() is called from the viewDidLoad function where the view is still not visible on the screen.

Try moving your code into the viewDidAppear and the viewWillTransition will be called.

This is my working code:

class ViewController: UIViewController {
    
    var forceLandscape: Bool = false
    
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        forceLandscape ? .landscape : .all
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        forceLandscape = true
        UIView.performWithoutAnimation {
            setNeedsUpdateOfSupportedInterfaceOrientations()
        }
    }
    
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        print("viewWillTransition")
    }
}
codingspark
  • 99
  • 1
  • 2
  • 1
    In the long run, though, you wouldn't want that code to run on _every_ call to `viewDidAppear` throughout the lifetime of the app, so you'd need a Bool flag to prevent that, so that it runs only once (the first time). – matt Sep 18 '22 at 16:12
  • Doesn't work! Try running the sample code *directly by touching the app icon* instead of XCode debugger. – Deepak Sharma Sep 18 '22 at 19:37