ios - 在 SwiftUI 中实现外部显示器支持

标签 ios swift swiftui ios13

我对通过 Airplay 和 SwiftUI 实现外部显示器支持感到困惑。

在 SceneDelegate.swift 中,我使用 UIScreen.didConnectNotification 观察者,它实际上检测到正在连接的新屏幕,但我无法将自定义 UIScene 分配给屏幕。

我发现了一些在 iOS12 及更低版本上使用 Swift 的好例子,但它们都不能在 SwiftUI 中工作,因为整个范例已更改为使用 UIScene 而不是 UIScreen。列表如下:

https://www.bignerdranch.com/blog/adding-external-display-support-to-your-ios-app-is-ridiculously-easy/

https://developer.apple.com/documentation/uikit/windows_and_screens/displaying_content_on_a_connected_screen

https://www.swiftjectivec.com/supporting-external-displays/

苹果甚至spoke about it last year

也许有些事情发生了变化,现在有一种新的方法可以正确地做到这一点。 此外,设置 UIWindow.screen = screen 在 iOS13 中已被弃用。

是否有人尝试过使用 SwiftUI 实现外部屏幕支持。非常感谢任何帮助。

最佳答案

我修改了 Big Nerd Ranch 博客中的示例,使其工作如下。

  1. 删除主要 Storyboard:我从新项目中删除了主要 Storyboard。在部署信息下,我将主界面设置为空字符串。

  2. 编辑 plist:在 plist 的“应用程序场景 list ”部分中定义两个场景(默认场景和外部场景)及其场景委托(delegate)。

    <key>UIApplicationSceneManifest</key>
    <dict>
        <key>UIApplicationSupportsMultipleScenes</key>
        <true/>
        <key>UISceneConfigurations</key>
        <dict>
            <key>UIWindowSceneSessionRoleApplication</key>
            <array>
                <dict>
                    <key>UISceneConfigurationName</key>
                    <string>Default Configuration</string>
                    <key>UISceneDelegateClassName</key>
                    <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
                </dict>
            </array>
            <key>UIWindowSceneSessionRoleExternalDisplay</key>
            <array>
                <dict>
                    <key>UISceneDelegateClassName</key>
                    <string>$(PRODUCT_MODULE_NAME).ExtSceneDelegate</string>
                    <key>UISceneConfigurationName</key>
                    <string>External Configuration</string>
                </dict>
            </array>
        </dict>
    </dict>
  • 编辑 View Controller 以显示简单的字符串:
  • class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = .blue
            view.addSubview(screenLabel)
        }
    
        var screenLabel: UILabel = {
            let label = UILabel()
            label.textColor = UIColor.white
            label.font = UIFont(name: "Helvetica-Bold", size: 22)
            return label
        }()
    
        override func viewDidLayoutSubviews() {
            /* Set the frame when the layout is changed */
            screenLabel.frame = CGRect(x: 0,
                                    y: 0,
                                    width: view.frame.width - 30,
                                    height: 24)
        }
    }
    
  • 修改 SceneDelegate 中的 scene(_:willConnectTo:options:) 以在主 (iPad) 窗口中显示信息。
  •     func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
            // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
            // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
            // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
            guard let windowScene = (scene as? UIWindowScene) else { return }
    
            window = UIWindow(frame: windowScene.coordinateSpace.bounds)
            window?.windowScene = windowScene
            let vc = ViewController()
            vc.loadViewIfNeeded()
            vc.screenLabel.text = String(describing: window)
            window?.rootViewController = vc
            window?.makeKeyAndVisible()
            window?.isHidden = false
        }
    
  • 为外部屏幕创建场景委托(delegate)。我创建了一个新的 Swift 文件 ExtSceneDelegate.swift,其中包含与 SceneDelegate.swift 相同的文本,并将类的名称从 SceneDelegate 更改为 ExtSceneDelegate。

  • 修改AppDelegate中的application(_:configurationForConnecting:options:)。其他人建议,如果您将其注释掉,一切都会好起来的。为了调试,我发现将其更改为:

  •     func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
    
            // This is not necessary; however, I found it useful for debugging
            switch connectingSceneSession.role.rawValue {
                case "UIWindowSceneSessionRoleApplication":
                    return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
                case "UIWindowSceneSessionRoleExternalDisplay":
                    return UISceneConfiguration(name: "External Configuration", sessionRole: connectingSceneSession.role)
                default:
                    fatalError("Unknown Configuration \(connectingSceneSession.role.rawValue)")
                }
        }
    
  • 在 iOS 上构建并运行该应用。您应该看到一个丑陋的蓝屏,其中包含有关 UIWindow 的信息。然后我使用屏幕镜像连接到 Apple TV。您应该会在外部屏幕上看到类似丑陋的蓝屏,并带有不同的 UIWindow 信息。
  • 对我来说,解决所有这些问题的关键引用是 https://onmyway133.github.io/blog/How-to-use-external-display-in-iOS/

    关于ios - 在 SwiftUI 中实现外部显示器支持,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58033818/

    相关文章:

    ios - Charles 代理 100% 节流

    ios - 如何在完成多个 UIWebView 请求后在 UITableViewCell 中找到事件指示器?

    ios - swift:重新加载 View 以在图表中显示新数据

    swift - SwiftUI-将边框添加到图像的一个边缘

    ios - iPad : need accurate alternative to NSTimer for making donkey kong style game

    ios - 如何阻止 Alamofire 对 URL 参数进行编码

    html - 如何在 Swift 3 中加载带有 HTML 的 CSS 文件?

    macos - SwiftUI:响应 macOS 上的应用程序终止

    ios - SwiftUI NavigationLink push in onAppear 使用@ObservableObject 时立即弹出 View

    ios - Sencha 字段集表单面板在 safari 中工作,而不是在 iOS 模拟器中工作