如果我在iOS 12设备上编译(不使用UIScene(,并将AirPlay镜像到我的Apple TV,则应用程序会按预期镜像到TV。
在iOS 13设备上,它似乎将其视为一个外部显示器,在那里它被格式化以适应屏幕(但我无法控制它(。
我更喜欢只镜像它的旧功能。
如何在iOS 13上完成镜像?我在文档中搜索:
application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration
在UISceneConfiguration
中有一个role
属性(当我尝试AirPlay Mirror时,它有UISceneSession.Role.windowExternalDisplay
(,但它似乎没有像UISceneSession.Role.windowMirror
那样的值。
我一直在尝试镜像和外部显示,只要代码/设置的正确组合,就有各种可能性,但某些功能似乎是不可能的。
在iOS 13下(使用iOS 13的Base SDK构建的应用程序(,您可以在外部显示器上镜像您的应用程序。但这样做会阻止你的应用程序在外部显示器上显示不同的内容。基本上,你的应用程序只镜像或只显示外部显示器的独特场景。
如果你只想镜像你的应用程序,那么请确保以下内容:
- 从应用程序代理中删除
application(_:configurationForConnecting:options:)
- 在Info.plist中,确保"应用程序场景清单"的"场景配置"部分下没有"外部显示会话角色"条目
如果这两件事都不是你应用程序的一部分,那么当你在iOS设备上激活屏幕镜像时,你的应用程序将简单地镜像到任何外部屏幕。
我自己刚刚遇到这个问题。我的解决方案实际上来自我的UIWindowSceneDelegate
类。
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
// External displays should not get assigned a window. When a window isn't assigned, the default behavior is mirroring.
guard session.role != .windowExternalDisplay else { return }
/* the rest of your setup */
}
如果不指定窗口,镜像似乎会成为默认选项。在那次更改之前,我的外部显示器(屏幕镜像(被赋予了自己独特的UIWindow实例。
我在任何地方都看不到这一点,而且它也不直观。正因为如此,我有点担心它将来会坏掉。
希望它仍然有帮助。
我发现使用Objective-C实现,可以通过在application:configurationForConnectingSceneSession:options:
中返回nil
来实现屏幕镜像行为。
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
if (connectingSceneSession.role == UIWindowSceneSessionRoleExternalDisplay) {
return nil;
}
UISceneConfiguration *configuration = [[UISceneConfiguration alloc] initWithName:@"Main" sessionRole:connectingSceneSession.role];
configuration.storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
configuration.delegateClass = [SceneDelegate class];
configuration.sceneClass = [UIWindowScene class];
return configuration;
}
请注意,这不是一种记录在案的方式,将来可能会中断。
编辑时间:在Swift中,您可以通过swizzling方法实现这一点:
@UIApplicationMain
class AppDelegate : UIResponder, UIApplicationDelegate {
override init() {
_ = AppDelegate.performSceneConfigurationSwizzle
super.init()
}
private static let performSceneConfigurationSwizzle: Void = {
method_exchangeImplementations(
class_getInstanceMethod(AppDelegate.self, #selector(AppDelegate.application(_:configurationForConnecting:options:)))!,
class_getInstanceMethod(AppDelegate.self, #selector(AppDelegate.swizzle_application(_:configurationForConnecting:options:)))!
)
}()
@objc func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
fatalError("Should never reach.")
}
@objc private func swizzle_application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration? {
if connectingSceneSession.role == .windowExternalDisplay {
return nil
}
// build scene configuration as usual…
}
}
不是在iOS 13中实现AppDelegate场景配置方法:
@available(iOS 13.0, *)
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
let configuration = UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
configuration.delegateClass = SceneDelegate.self
return configuration
}
我转而使用Info.plist变体(并删除了上面的代码(,在这里您可以在Info.plist中有效地指定以上所有内容。(对于Info.plist文件中预期内容的最新版本,只需在Xcode中创建一个新项目,并从新的Info.plist中复制Application Scene Manifest
键的内容即可(。
它现在工作得很好,AirPlay镜像如预期。我确实尝试过将role
更改为windowApplication
,就像iOS对Info.plist变体所做的那样,但它仍然不起作用。