iOS 16 beta中强制视图控制器方向是否可用?

22
根据 iOS & iPadOS 16 Beta 3 的发布说明:尝试通过 setValue:forKey: 在 UIDevice 上设置方向不再受支持且已不起作用。相反,建议使用:preferredInterfaceOrientationForPresentation。
在我的情况下,在 iOS 16 beta 中通过使用 preferredInterfaceOrientationForPresentationrequestGeometryUpdate 强制视图控制器方向也无效。
以前,UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation") 可以正常工作。

1
和你一样的困境 :( 在 iOS 和 iPadOS 16 Beta 4 上, 它显示 “已修复”, 但我测试了还是一样,控制台仍然显示错误信息: [方向] UIKIT 的客户端 BUG: 设置 UIDevice.orientation 不受支持。请使用 UIWindowScene.requestGeometryUpdate(_:)。 - enghong
你找到了解决方案吗? - sudoExclaimationExclaimation
有能力完成这项工作的Stack Overflow大神可以在这里提供帮助吗?期待您的帮助。 - Chetan Prajapati
这可能对您有帮助,请参考此答案 https://dev59.com/XlEG5IYBdhLWcg3wH0fT#76329324 - Amrit Tiwari
8个回答

16

这对我有效。

在AppDelegate中,

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

在视图控制器中,

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

UIApplication.navigationTopViewController()?.setNeedsUpdateOfSupportedInterfaceOrientations()

在帮助器中,
extension UIApplication {
    class func navigationTopViewController() -> UIViewController? {
        let nav = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController
        return  nav?.topViewController
    }
}

1
更新了答案。我在这里添加了我的辅助程序。 - J.Y.527
我确认这个解决方案有效,并且帮助了我解决了问题。 - ferus
我确认,这个有效。 - Jeffrey
@J.Y.527,您介意我在编辑中提供等效的 C# 代码(适用于 Xamarin/MAUI)吗? - Eli
3
@J.Y.527 我该如何在SwiftUI视图中使其工作? - NNN
它对我有效。我的问题在AppDelegate中。非常感谢! - Yulia

7
我的问题是,当关闭模态视图时,我尝试进行操作,但视图更新得不够快。如果我将requestGeometryUpdate放在单独的按钮上,那么在关闭视图时它可以正常工作。
if #available(iOS 16.0, *) {

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

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

} 

你遇到了完全相同的问题。你找到解决方案了吗? - Bonan

3

对我来说它有效:

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

如何使用?

只需在您的UIViewController上调用它:

setDeviceOrientation(orientation: .landscapeRight)

编辑

更完整的解决方案:

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

如何使用:

 DeviceOrientation.shared.set(orientation: .portrait)

你检查过 iOS 16.1 吗? - Vahid
是的,它可以工作。你有问题吗? - Gus
你在AppDelegate中应用了supportedInterfaceOrientationsFor吗?在目标的部署信息中,你勾选了哪些方向?你有没有一个我们可以检查的示例项目?谢谢。 - cesarcarlos
我已经在supportedInterfaceOrientationsFor中设置了所有方向,但这并没有强制改变方向的效果。 - kelalaka

3

我尝试了上面的所有解决方案,似乎它们都不是100%有效的。在查看https://developer.apple.com/forums/thread/707735后,我得到了提示。让我们尝试下面的代码。这对我有效。

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

1
我注意到我的问题似乎已经通过调用以下方法得到了解决:

[UIViewController setNeedsUpdateOfSupportedInterface

你可以尝试一下。

1
你把这个放在哪里调用?如果我把它放在 viewDidLoad 中,我的代码不起作用。 - sudoExclaimationExclaimation
不适用于我。 - Bruno Vaillant
1
一样的问题,不起作用。有什么技巧吗?iOS 16感觉很糟糕。;) - Chetan Prajapati

0

苹果发布了新的API,取代了setValue:forKey:"orientation"。 苹果更新

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

但是我遇到了一个关于UIDevice.orientationDidChangeNotification的问题,它无法正常工作。


0

根据苹果的文档,您需要:

使用指定的几何偏好对象请求更新窗口场景的几何形状。

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

因此,使用示例中的代码,您可以使用requestGeometryUpdatesetNeedsUpdateOFSupportedInterface更改我们在视图中设置方向的方式。

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

0

setValue:forKey 是旧版 NSObject (NSKeyValueCoding) 的一个方法。它并没有被官方文档记录,也不被 UIDevice 类支持。使用它相当于使用私有 API。苹果公司随时可能终止其使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接