20

According to the iOS & iPadOS 16 Beta 3 Release Notes:- Attempting to set an orientation on UIDevice via setValue:forKey: isn’t supported and no longer works. Instead, they say use: preferredInterfaceOrientationForPresentation.

In my case, force view controller orientation is not working in iOS 16 beta either by using preferredInterfaceOrientationForPresentation or requestGeometryUpdate.

Previously, UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation") was working fine.

Abhayam
  • 327
  • 1
  • 3
  • 6
  • 1
    Having same situation as you :( On iOS & iPadOS 16 Beta 4, it said "FIXED" but I tested and still the same, and console still display error message: [Orientation] BUG IN CLIENT OF UIKIT: Setting UIDevice.orientation is not supported. Please use UIWindowScene.requestGeometryUpdate(_:) – enghong Aug 01 '22 at 01:58
  • Did you find any solution to this? – sudoExclaimationExclaimation Aug 29 '22 at 09:41
  • https://stackoverflow.com/questions/73727291/ios16-present-uiviewcontroller-in-landscape-only-for-single-screen-not-working/73742232 man of the stack overflow who can do the job can help here please.! Help awaited. – Chetan Prajapati Sep 16 '22 at 13:10
  • May be helpful for you, refer this answer https://stackoverflow.com/a/76329324/3887987 – Amrit Tiwari May 25 '23 at 06:52

8 Answers8

15

It works for me.

In AppDelegate,

var orientation: UIInterfaceOrientationMask = .portrait
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return orientation
}

In view controller,

(UIApplication.shared.delegate as? AppDelegate)?.orientation = .landscapeRight
                    
let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
windowScene?.requestGeometryUpdate(.iOS(interfaceOrientations: .landscapeRight))

UIApplication.navigationTopViewController()?.setNeedsUpdateOfSupportedInterfaceOrientations()

In Helper,

extension UIApplication {
    class func navigationTopViewController() -> UIViewController? {
        let nav = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController
        return  nav?.topViewController
    }
}
Bhadresh Kathiriya
  • 3,147
  • 2
  • 21
  • 41
J.Y.527
  • 171
  • 6
6

My problem with my code below is that I'm trying to do it when closing a modal view and the view under it are not updated quick enough. If I put the requestGeometryUpdate on a separate button then when I close the view it work.

if #available(iOS 16.0, *) {

    let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene

    windowScene?.requestGeometryUpdate(.iOS(interfaceOrientations: .portrait))

} 
Bruno Vaillant
  • 441
  • 3
  • 5
3

I tried all above solutions seem they're not 100% percent working. After through this post https://developer.apple.com/forums/thread/707735 i got the hint. Let's try this below code. It’s worked for me.

if #available(iOS 16.0, *) {
        DispatchQueue.main.async {
            UIViewController.attemptRotationToDeviceOrientation()
                
            let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
                windowScene?.requestGeometryUpdate(.iOS(interfaceOrientations: orientation)) { error in
                print(error)
                print(windowScene?.effectiveGeometry)
            }
               navigationController?.topViewController?.setNeedsUpdateOfSupportedInterfaceOrientations()
        }
   }
Trinh Tran
  • 306
  • 3
  • 7
2

It works for me:

import Foundation
import UIKit

extension UIViewController {
    
    func setDeviceOrientation(orientation: UIInterfaceOrientationMask) {
        if #available(iOS 16.0, *) {
            let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
            windowScene?.requestGeometryUpdate(.iOS(interfaceOrientations: orientation))
        } else {
            UIDevice.current.setValue(orientation.toUIInterfaceOrientation.rawValue, forKey: "orientation")
        }
    }
}

extension UIInterfaceOrientationMask {
    var toUIInterfaceOrientation: UIInterfaceOrientation {
        switch self {
        case .portrait:
            return UIInterfaceOrientation.portrait
        case .portraitUpsideDown:
            return UIInterfaceOrientation.portraitUpsideDown
        case .landscapeRight:
            return UIInterfaceOrientation.landscapeRight
        case .landscapeLeft:
            return UIInterfaceOrientation.landscapeLeft
        default:
            return UIInterfaceOrientation.unknown
        }
    }
}

How to use it?

Just call it on your UIViewController:

setDeviceOrientation(orientation: .landscapeRight)

EDIT

More completed solution:

import UIKit

final class DeviceOrientation {
    
    static let shared: DeviceOrientation = DeviceOrientation()
    
    // MARK: - Private methods
    
    private var windowScene: UIWindowScene? {
        return UIApplication.shared.connectedScenes.first as? UIWindowScene
    }
    
    // MARK: - Public methods
    
    func set(orientation: UIInterfaceOrientationMask) {
        if #available(iOS 16.0, *) {
            windowScene?.requestGeometryUpdate(.iOS(interfaceOrientations: orientation))
        } else {
            UIDevice.current.setValue(orientation.toUIInterfaceOrientation.rawValue, forKey: "orientation")
        }
    }
    
    var isLandscape: Bool {
        if #available(iOS 16.0, *) {
            return windowScene?.interfaceOrientation.isLandscape ?? false
        }
        return UIDevice.current.orientation.isLandscape
    }
    
    var isPortrait: Bool {
        if #available(iOS 16.0, *) {
            return windowScene?.interfaceOrientation.isPortrait ?? false
        }
        return UIDevice.current.orientation.isPortrait
    }
    
    var isFlat: Bool {
        if #available(iOS 16.0, *) {
            return false
        }
        return UIDevice.current.orientation.isFlat
    }
}

extension UIInterfaceOrientationMask {
    var toUIInterfaceOrientation: UIInterfaceOrientation {
        switch self {
        case .portrait:
            return UIInterfaceOrientation.portrait
        case .portraitUpsideDown:
            return UIInterfaceOrientation.portraitUpsideDown
        case .landscapeRight:
            return UIInterfaceOrientation.landscapeRight
        case .landscapeLeft:
            return UIInterfaceOrientation.landscapeLeft
        default:
            return UIInterfaceOrientation.unknown
        }
    }
}

How to use it:

 DeviceOrientation.shared.set(orientation: .portrait)
Bhadresh Kathiriya
  • 3,147
  • 2
  • 21
  • 41
Gus
  • 856
  • 1
  • 8
  • 24
1

I noticed my issue seems like resolved by calling method below:

[UIViewController setNeedsUpdateOfSupportedInterface

You may give it a try.

enghong
  • 111
  • 4
0

setValue:forKey is a method of old NSObject (NSKeyValueCoding). It's not official documented and supported by UIDevice class. Using it is considering using a private api. Apple can terminate it anytime they want.

Quang Hà
  • 4,613
  • 3
  • 24
  • 40
0

Apple released new API which is replaced with setValue:forKey:"orientation". Apple update

guard let windowScene = view.window?.windowScene else { return }
windowScene.requestGeometryUpdate(.iOS(interfaceOrientations: .landscape)) { error in
// Handle denial of request.
}

But I am having problem about UIDevice.orientationDidChangeNotification , it is not working

Kimtungit
  • 1
  • 1
0

Following Apple's documentation you need to

Requests an update to the window scene’s geometry using the specified geometry preferences object.

https://developer.apple.com/documentation/uikit/uiwindowscene/3975944-requestgeometryupdate/

So using the code in the example, you can change the way we set the orientation in our views using requestGeometryUpdate and using as well setNeedsUpdateOFSupportedInterface

public extension UIViewController {

func deviceOrientation(orientation: UIInterfaceOrientationMask) {
    if #available(iOS 16.0, *) {
        guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
        else { return }
        windowScene.requestGeometryUpdate(.iOS(interfaceOrientations: orientation))
        self.setNeedsUpdateOfSupportedInterfaceOrientations()
    }
  }
}
Sophy Swicz
  • 1,307
  • 11
  • 21