SceneDelegate In Xcode11
参考The Scene Delegate In Xcode 11 And iOS 13
App Delegate
其中的application(:didFinishLaunchingWithOptions:)函数是系统开始应用后调用的第一个函数,继承自UIApplicationDelegate协议
在iOS12时,我们在appDelegate中做的事情:
- 设置app的第一个
view controller - 配置应用程序设置和启动组件,例如日志记录和云服务等
- 注册app的推送处理,以及响应发送到app的推送的处理
- 响应对app的生命周期事件,例如进入后台、启动app、退出app等
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
{
let timeline = TimelineViewController()
let navigation = UINavigationController(rootViewController: timeline)
let frame = UIScreen.main.bounds
window = UIWindow(frame: frame)
window!.rootViewController = navigation
window!.makeKeyAndVisible()
return true
}
创建一个ViewController,放入navigation controller中,设置为UIWindow对象的的rootViewController。这个window是appdelegate 的属性,并且是app拥有的一个window。
window是一个重要的概念,本质上window就是我们的应用程序,大多数iOS应用只有这一个window,它包含了app的用户界面,将event事件分发到view中,并且提供了展示app内容的主要背景。
Scene Delegate
在iOS13以上,scene delegate接管了app delegate的一些角色。最重要的是,window的概念已经被scene取代。一个应用程序可以有多个场景,而一个场景现在可以用作app的用户界面和内容的背景。
具有多场景的app概念很有趣,可以允许你在iOS和iPadOS中构建多窗口应用程序。例如,文字处理器应用程序中的每个文本文档都可以有自己的场景。用户还可以创建场景副本,一次有效的运行一个app的多个实例。
在Xcode11实际使用中,使用sceneDelegate的位置:
- 一个新的iOS项目具有
SceneDelegate类,该类会自动创建,其中包含常用的声明周期事件,例如active、resign和disconnect Appdelegate类具有和Scene相关的两个函数application(_:configurationForConnecting:options :)和application(_:didDiscardSceneSessions :)- Info.plist文件中有
Application Scene Manifest,列出此应用程序中包含的Scene,包括其class、delegate、storyboard名称
Scene Delegate Class
在SceneDelegate类中,最重要的是scene(_:willConnectTo:options:).函数,某些方面来说,其重要性与iOS12的application(_:didFinishLaunchingWithOptions:)类似。当场景添加到App中后,该函数会被调用,因此这是配置该Scene的理想位置。
需要注意SceneDelegate也使用delegate,并且通常一个delegate会响应任何场景,使用一个delegate去配置应用程度的所有场景
在SceneDelegate中包含以下函数:
sceneDidDisconnect(_:)当一个场景与应用程序断开连接时被调用(注意场景以后可以重连)sceneDidBecomeActive(_:)当用户开始和场景交互时调用。(例如当从应用程序切换器中选择时)sceneWillResignActive(_ :)当用户停止和场景交互时调用(例如切换到另外一个场景时)sceneWillEnterForeground(_ :)当场景进入前台时,即从背景状态恢复时,调用sceneDidEnterBackground(_ :)当场景进入后台时调用,此时应用已经最小化并存在于后台
这些函数是应用程序的典型生命周期事件
Appdelegate: Scene Sessions
iOS13的AppDelegate,包含了两个scene session管理相关的delegate函数。当app中创建scene后,scene session将会跟踪和该场景相关的所有信息
Application(_:configurationForConnecting:options :)在创建新场景时需要返回配置对象Application(_:didDiscardSceneSessions :)当app的用户通过应用切换器关闭一个或者多个场景时调用
目前,Scene session用于指定场景角色,例如External Display或者CarPlay。还可以用于还原scene状态。状态还原在应用启动之间保留并重新创建UI。还可以将用户信息分配给场景会话,这实际是一个可以放入任何内容的字典
application(_:didDiscardSceneSessions :),在app的用户通过应用切换器关闭一个或者多个场景时调用。可以使用此函数处理这些scene所需要的资源,因为不再需要这些资源。
application(_:didDiscardSceneSessions :)与sceneDidDisconnect(_ :)对比,后者只会在场景断开连接时调用,但是并不一定会被丢弃,因为可以重新连接,而application(_:didDiscardSceneSessions :)标记为使用应用程序切换器退出场景的时候
info.plist Application Scene Manifest
app支持的每个scene都需要在Application Scene Mainifest中声明。大多数app只有一个场景,但是可以创建更多的场景,例如用于响应推送通知或者特定操作的特定场景
注意:声明的时session type而不是session本身。因此应用程序可以支持一个场景,创建该场景的副本,然后使用该场景副本创建多窗口应用程序
//Object-C版本

//swift版本

顶层入口为Application Scene Mainifest,而在其下为Enable Multiple Windows,需要设置为YES来支持多窗口应用程序。下面是Scene Configuration,其中Application Session Role用于声明app内部的scene,而还有另外一个External Display Session Role用于声明屏幕外的场景。
其中最重要的为Application Session Role数组:
configuration名字,必须为唯一的- 场景的类名称
- 场景的代理类名称,通常为
SceneDelegate - 包含初始UI的
storyboard名称
而SceneDelegate、Appdelegate中的scene session、以及Application Scene Manifest是怎么协同工作来创建多窗口应用呢?
- SceneDelegate类 管理场景的声明周期
- Appdelegate中有新功能,管理场景会话、提供场景的配置数据
Scene Delegate 和 SwiftUI
最简单的引导iOS13应用程序的方法时使用SwiftUI,而SwiftUI App也主要依靠SceneDelegate来设置app的初始UI

- 并没有设置StoryBoard名称
- 如果支持多窗口 需要设置
Enable Mutiple Window为YES
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = ContentView()
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
...
scene(_:willConnectTo:options :)代理函数在将新场景添加到应用后调用。提供了一个Scene(以及session).此UIWindowScene是由App创建的,因此无需手动进行操作- 还有使用window属性,但是现在已经成为Scene的一部分。在
if let代码块中,使用场景初始化UIWindow对象 - 设置了根视图控制器,将window设置给window属性,并且将该窗口
makeKeyAndVisible,即将该窗口设置于App的UI前 - ContenView是特定于SwiftUI进行创建的,通过
UIHostingController将其添加为根视图控制器,将基于SwiftUI视图显示到屏幕上 - 请注意:
UIScene类型的Scene参数是UIWindowScene类型的,但是使用as?类型转换(猜测虽然目前场景大多为UIWindowScene类型,但是将来会有更所类型场景)
SceneDelegate With Storyboards
在未来虽然有可能看到更多SwiftUI应用,但是目前来说storyboards更加常见
目前使用Storyboard我们无需做任何操作,只需要File → New → Project…然后选择singleApp,最后选择Storyboard for User Interface就可以了
- 此时可以再Application Scene Manifest找到Main Storyboard
- 默认情况下,App Delegate将会使用
Default scene configuration - 默认情况下,SceneDelegate设置一个UIWindow对象,并使用
Main.STORYBOARD创建初始UI
自己编程方式设置App
当我们不使用storyboards单独使用Xib创建AppUI,此时在SceneDelegate的scene(_:willConnectTo:options :)函数中设置初始视图控制器
class SceneDelegate: 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)
let timeline = TimelineViewController()
let navigation = UINavigationController(rootViewController: timeline)
window.rootViewController = navigation
self.window = window
window.makeKeyAndVisible()
}
}
...
- 使用
windowScene对象(由scene参数通过类型转换)初始化window属性, - 在
if letblock中的代码 与iOS12或者更低版本设置视图控制器的方式类似。初始化带导航控制器的视图控制器,将其分配给rootViewController属性 - 最后将window常量,设置给window属性,并将其设置为
keyAndVisible,使其在屏幕最前方
这种方式设置SceneDelegte只是将代码从Appdelegate移过来 并且配置
Application Scene Manifest.即可
Scene-Based 声明周期
添加对场景的支持会更改您的应用对生命周期事件的响应方式。在没有Scene的App中,App delegate对象处理到前台或后台的过渡。当您向App添加Scene支持时,UIKit会将职责转移到scene delegate对象上。Scene的生命周期彼此独立,并且独立于应用程序本身,因此scene delegate对象必须处理过渡。
如果您的应用程序还支持iOS 12或更低,则可以在App delegate和scene delegate对象中处理生命周期过渡。 UIKit仅只会通知一个delegate对象。在iOS 13及更高版本中,UIKit会通知您的scene delegate对象。在iOS 12及更低版本中,UIKit会通知您的App delegate。
有关如何处理生命周期事件的信息,请参阅管理应用程序的生命周期