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 let
block中的代码 与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
。
有关如何处理生命周期事件的信息,请参阅管理应用程序的生命周期