iOS中使用UIScenes 13、AirPlay如何镜像屏幕(好像默认是外接显示器)
Using UIScenes in iOS 13, how do I AirPlay Mirror a screen (seems to default to external display)
如果我编译到 iOS 12 设备(不使用 UIScene)和 AirPlay Mirror 到我的 Apple 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
.
而不是实现iOS13中的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 Mirror 可以按预期进行镜像。我确实尝试将 role
更改为 windowApplication
,因为 iOS 似乎对 Info.plist 变体进行了更改,但它仍然不起作用。
我一直在尝试镜像和外接显示器,只要 code/settings 的正确组合就存在各种可能性,但某些功能似乎无法实现。
在 iOS 13 下(使用 iOS 13 的 Base SDK 构建的应用程序),您可以将您的应用程序镜像到外部显示器上。但是,使这项工作能够阻止您的应用程序在外部显示器上显示不同的内容。基本上您的应用仅镜像或仅显示外部显示器的独特场景。
如果您希望只镜像您的应用,请确保满足以下条件:
- 从您的 App Delegate 中删除
application(_:configurationForConnecting:options:)
。
- 在 Info.plist 中,确保 "Application Scene Manifest" 的 "Scene Configuration" 部分下没有 "External Display Session Role" 条目。
如果这两个东西都不是您的应用程序的一部分,那么当您在 iOS 设备上激活屏幕镜像时,您的应用程序将简单地镜像到任何外部屏幕。
我发现通过 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 中,您可以通过 method 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…
}
}
我自己 运行 进入这个问题。我的解决方案实际上来自我的 UIWindowSceneDelegate
class.
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 */
}
当您不分配 window 时,镜像似乎成为默认选项。在此更改之前,我的外部显示器(屏幕镜像)被赋予了自己独特的 UIWindow 实例。
我没有在任何地方看到这个记录,而且它不直观。因此,我有点担心它以后会坏掉。
希望它仍然有帮助。
如果我编译到 iOS 12 设备(不使用 UIScene)和 AirPlay Mirror 到我的 Apple 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
.
而不是实现iOS13中的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 Mirror 可以按预期进行镜像。我确实尝试将 role
更改为 windowApplication
,因为 iOS 似乎对 Info.plist 变体进行了更改,但它仍然不起作用。
我一直在尝试镜像和外接显示器,只要 code/settings 的正确组合就存在各种可能性,但某些功能似乎无法实现。
在 iOS 13 下(使用 iOS 13 的 Base SDK 构建的应用程序),您可以将您的应用程序镜像到外部显示器上。但是,使这项工作能够阻止您的应用程序在外部显示器上显示不同的内容。基本上您的应用仅镜像或仅显示外部显示器的独特场景。
如果您希望只镜像您的应用,请确保满足以下条件:
- 从您的 App Delegate 中删除
application(_:configurationForConnecting:options:)
。 - 在 Info.plist 中,确保 "Application Scene Manifest" 的 "Scene Configuration" 部分下没有 "External Display Session Role" 条目。
如果这两个东西都不是您的应用程序的一部分,那么当您在 iOS 设备上激活屏幕镜像时,您的应用程序将简单地镜像到任何外部屏幕。
我发现通过 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 中,您可以通过 method 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…
}
}
我自己 运行 进入这个问题。我的解决方案实际上来自我的 UIWindowSceneDelegate
class.
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 */
}
当您不分配 window 时,镜像似乎成为默认选项。在此更改之前,我的外部显示器(屏幕镜像)被赋予了自己独特的 UIWindow 实例。
我没有在任何地方看到这个记录,而且它不直观。因此,我有点担心它以后会坏掉。
希望它仍然有帮助。