由于公司业务布局变化,接到了制作AppleTV项目的任务。在制作的过程中,发现了一款APP可以随时将APP中自定义的内容投屏到电视/Mac/显示器等终端。这块技术对以后的业务布局可能会有帮助,决定研究一下。
最后功能简单实现,但是中间还有很多的细节没弄懂,以后有时间了再研究一下。
iOS13引入了SceneDelegate,使APP业务增加了多场景,iPhone-外接屏(TV/Mac/投影仪),在AppDelegate中新增了代理方法configurationForConnecting,能够监听到外部屏幕接入的状态。
整理一下基本的代码逻辑就是:
- application:configurationForConnecting:options 监听到外部进入了屏幕,分析并返回不同场景的配置信息(包括屏幕代理是谁)。
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
if connectingSceneSession.role == .windowApplication {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
} else {
let config = UISceneConfiguration(name: "Screen Configuration", sessionRole: connectingSceneSession.role)
config.delegateClass = ScreenDelegate.self
return config
}
}
我一直没有拿到options中的信息,connectingSceneSession中的role来区分是默认场景还是新接入的场景。 这里做好判断之后默认场景不需要额外的配置,新接入的场景需要指定一个config name,并且将新建的代理类提供给config,是为了让新的场景的生命周期等在你指定的类中回调。这里我新建的代理类是 ScreenDelegate 2. scene:willConnectTo:options 在指定的屏幕代理类中实现根视图设置。
class ScreenDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = ScreenViewController()
window.makeKeyAndVisible()
self.window = window
}
}
func sceneDidDisconnect(_ scene: UIScene) {
print("场景断开连接")
}
}
注意,新建的代理类是继承UIResponder,并且要遵守UIWindowSceneDelegate协议。 ScreenViewController是要显示在外部屏幕上的根视图控制器。
调试:APP运行之后,找到镜像选中显示屏,显示ScreenViewController了内容,完成。
存疑:
- 在网上找到了很多不教程,包括说如果在info.plist中添加了配置项就不要再application:configurationForConnecting:options中设置实现了,然后我尝试了不实现代理方法是显示不出来的。
- self.window = window 一定要写这句代码,否则也是显示不出来
- 我还使用了UIScreen.didConnectNotification监听,通过每次监听获取外界屏幕的状态变化,然后在通过UIApplication.shared.connectedScenes新增的Scene中的screen和监听到的screen做对比之后设置screen的方法,但是在SwiftUI中大概是因为导入了Combine框架,UIApplication.shared.connectedScenes能准确获取到新增的场景,但是在纯Swift中就无法准确的获取到,可能是都放到application:configurationForConnecting:options中来实现了。