使用单个 ViewController 动态切换设备方向

Dynamically switching device orientation with single ViewController

我目前正在开发一个应用程序,它有一个 ViewController 和一个 WKWebView 对象,充当网络应用程序的客户端。它使用 JavaScript 通信和注入以允许本机 Swift 中的对象与 WebView 交互。

我的Objective

我有一个函数 urlDidChange(_ url: String),它会在 WKWebView 的原始 URL 值发生变化时触发。我正在尝试根据上述新 url 的值动态设置方向限制,并在满足条件后强制旋转设备以适应这些限制。

我不确定这个额外的信息是否重要,但我认为无论如何我都会包括它:UIViewController 也嵌入在 UINavigationController 中。本机 NavBar 确实帮助客户感觉更像本机应用程序。我没有为它设置任何自定义 类,只是一直在使用 let navBar = navigationController?.navigationBar.

所需示例用法

func urlDidChange(_ url: String) {
    if url.contains("/dashboard") {
        UIInterfaceOrientationMask = .portrait
    } else if url.contains("/builder") {
        UIInterfaceOrientationMask = [.portrait, .landscapeRight]
    } else {
        UIInterfaceOrientationMask = .all
    }
    // Set new orientation properties
    // Force device rotation based on newly set properties
    UIViewController.attemptRotationToDeviceOrientation()   
}

当前代码

这是我当前的设置。我尝试了以下操作,但没有成功:

enum Page {
    
    var orientation: UIInterfaceOrientationMask {
        switch self {
        case .login:        return getDeviceOrientation()
        case .dashboard:    return getDeviceOrientation()
        case .newProject:   return getDeviceOrientation()
        case .builder:      return getDeviceOrientation()
        case .other:        return getDeviceOrientation()
        }
    }
    
    func getDeviceOrientation() -> UIInterfaceOrientationMask {
        let phone = Device.isPhone()
        switch self {
        case .login:
            if phone { return .portrait }
            else { return .all }
        case .dashboard:
            if phone { return .portrait }
            else { return .all }
        case .newProject:
            if phone { return .portrait }
            else { return .all }
        case .builder:
            if phone { return [.portrait, .landscapeRight] }
            else { return .all }
        case .other:
            if phone { return .portrait }
            else { return .all }
        }
    }

ViewController

最后,为了应用新的方向属性,我使用了这个:

func urlDidChange(_ url: String) {
    let page = Page.get(forURL: url)    // Returns Page case for current URL
    
    UIDevice.current.setValue(page.orientation.rawValue, forKey: "orientation")
    UIViewController.attemptRotationToDeviceOrientation() 
}

奖金问题

是否有更有效的方法使用枚举将设备属性与我的页面枚举结合起来(即 case .builder 具有不同的 return 值,当 Device.isPhoneDevice.isPad)?

如您在上面的代码中所见,我只是使用 if 语句来确定现在提供哪个输出:

case .builder:
    if phone { return [.portrait, .landscapeRight] }
    else { return .all }

如果您的 ViewController 在 UINavigationController 中,这可能就是原因。您可以尝试使用自定义 UINavigationController,它可以从您的 ViewController 检索方向吗?像这样:

struct OrientationConfig {
    let phone: UIInterfaceOrientationMask
    let pad: UIInterfaceOrientationMask

    static let defaultConfig = OrientationConfig(phone: .portrait, pad: .all)
}

enum Page {
    case login
    case dashboard
    case newProject
    case builder
    case other

    static let orientationConfigs: [Page:OrientationConfig] = [
        .login: OrientationConfig.defaultConfig,
        .dashboard: OrientationConfig.defaultConfig,
        .newProject: OrientationConfig.defaultConfig,
        .builder: OrientationConfig(phone: [.portrait, .landscapeRight],
                                    pad: .all),
        .other: OrientationConfig.defaultConfig
    ]
}

class MyViewController: UIViewController {

    var page: Page = .login

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if let config = Page.orientationConfigs[page] {
            return Device.isPhone ? config.phone : config.pad
        }
        return super.supportedInterfaceOrientations
    }

}

class MyNavigationController: UINavigationController {
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if let viewController = topViewController as? MyViewController {
            return viewController.supportedInterfaceOrientations
        }
        return super.supportedInterfaceOrientations
    }
}