is Force View Controller Orientation working in iOS 16 beta
Asked Answered
S

8

23

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.

Sennight answered 26/7, 2022 at 13:35 Comment(4)
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(_:)Pensioner
Did you find any solution to this?Dewar
#73727791 man of the stack overflow who can do the job can help here please.! Help awaited.Salience
May be helpful for you, refer this answer https://mcmap.net/q/160852/-ios-16-scene-orientation-issueAila
L
16

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
    }
}
Lennie answered 15/9, 2022 at 18:39 Comment(6)
updated the answer. I added my helper here.Lennie
I confirm that this solution is working and helped me with my problem.Recollection
i confirm , this is working.Pentamerous
@Lennie Would you mind me giving the equivalent C# code (for Xamarin/ MAUI) as an edit ?Ironbark
@Lennie how would i get this to work from a swiftUI view?Candlestick
It's working for me. My problem was in AppDelegate. Thanks a lot!Bushido
P
7

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))

} 
Priebe answered 31/8, 2022 at 15:9 Comment(1)
Having exactly the same issue. Did you find a solution?Polyhydroxy
I
3

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)
Ildaile answered 7/10, 2022 at 13:44 Comment(4)
Did you check ios 16.1?Traherne
Yes, it works. Do you have problems?Ildaile
Are you applying supportedInterfaceOrientationsFor in AppDelegate? What orientations do you have checked in the Target's deployment info? Do you by any chance have a sample project we could check? Thanks.Dusk
I've set all orientations on supportedInterfaceOrientationsFor and this did not work to force the orientation..Bangs
N
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()
        }
   }
Nectar answered 29/10, 2022 at 14:45 Comment(0)
P
1

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

[UIViewController setNeedsUpdateOfSupportedInterface

You may give it a try.

Pensioner answered 5/8, 2022 at 6:23 Comment(3)
Where do you call this? Mine doesn't work with this if I place it in the viewDidLoadDewar
Does not work for mePriebe
Same here, not works. Any hack? iOS 16 feels terrible. ;)Salience
H
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.

Holliman answered 5/8, 2022 at 6:28 Comment(0)
C
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

Corporative answered 15/9, 2022 at 10:25 Comment(0)
B
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()
    }
  }
}
Brassbound answered 25/10, 2022 at 15:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.