iOS13 項(xiàng)目中的SceneDelegate
類有什么作用?自從Xcode11發(fā)布以來(lái),當(dāng)你使用新XCode創(chuàng)建一個(gè)新的iOS項(xiàng)目時(shí),SceneDelegate
會(huì)被默認(rèn)創(chuàng)建绰姻,它到底有什么用呢。
在本文中引瀑,我們將深入探討iOS 13和Xcode 11的一些變化狂芋。我們將重點(diǎn)關(guān)注SceneDelegate和AppDelegate,以及它們?nèi)绾斡绊慡wiftUI憨栽、Storyboard和基于XIB的UI項(xiàng)目帜矾。
通過(guò)閱讀本文你將了解到:
- SceneDelegate和AppDelegate的新變化
- 他們是如何合作引導(dǎo)你的app啟動(dòng)的
- 在純手寫(xiě)App中使用
SceneDelegate
- 在Storyboards 和 SwiftUI項(xiàng)目中使用
SceneDelegate
讓我們開(kāi)始吧。
本篇文章基于 Xcode 11 和 iOS 13.
AppDelegate
你可能對(duì)AppDelegate已經(jīng)熟悉屑柔,他是iOS app的入口屡萤,application(_:didFinishLaunchingWithOptions:)
是你的app啟動(dòng)后系統(tǒng)調(diào)用的第一個(gè)函數(shù)。
AppDelegate
類實(shí)現(xiàn)了UIKit庫(kù)中的UIApplicationDelegate
協(xié)議掸宛。而到了iOS13 AppDelegate
的角色將會(huì)發(fā)生變化死陆,后面我們會(huì)詳細(xì)討論。
下面是你在iOS12中一般會(huì)在AppDelegate中做的事情:
- 創(chuàng)建app的第一個(gè)view controller也就是 rootViewController
- 配置并啟動(dòng)一些像日志記錄和云服務(wù)之類的組件
- 注冊(cè)推送通知處理程序唧瘾,并響應(yīng)發(fā)送到app的推送通知
- 響應(yīng)應(yīng)用程序生命周期事件措译,例如進(jìn)入后臺(tái)别凤,恢復(fù)應(yīng)用程序或退出應(yīng)用程序(終止)
iOS12及以前,使用Storyboards的app领虹,AppDelegate很簡(jiǎn)單规哪。 像這樣:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
{
return true
}
一個(gè)使用XIB的簡(jiǎn)單應(yīng)用看起來(lái)像這樣:
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
}
在上面的代碼中,我們創(chuàng)建一個(gè)ViewController,并將其放在navigation controller中掠械。然后將其分配給UIWindow對(duì)象的rootViewController屬性由缆。 這個(gè)window
對(duì)象是AppDelegate的屬性注祖,它是我們的應(yīng)用的一個(gè)窗口猾蒂。
應(yīng)用程序的window是一個(gè)重要的概念。 本質(zhì)上是晨,窗口就是應(yīng)用程序肚菠,大多數(shù)iOS應(yīng)用程序只有一個(gè)窗口。 它包含您應(yīng)用的用戶界面(UI)罩缴,將事件調(diào)度到視圖蚊逢,并提供了一個(gè)主要背景層來(lái)顯示您的應(yīng)用內(nèi)容。 從某種意義上說(shuō)箫章,“ Windows”的概念就是微軟定義的窗口烙荷,而在iOS上,這個(gè)概念沒(méi)有什么不同檬寂。 (謝謝终抽,Xerox!)
好了桶至,下面讓我們繼續(xù)SceneDelegate昼伴。
如果“窗口”的概念仍然不了解,請(qǐng)查看iPhone上的應(yīng)用程序切換器镣屹。 雙擊Home鍵或從iPhone底部向上滑動(dòng)圃郊,然后您會(huì)看到當(dāng)前正在運(yùn)行的應(yīng)用程序的窗口。 這就是應(yīng)用程序切換器女蜈。
The Scene Delegate
在iOS 13(及以后版本)上持舆,SceneDelegate將負(fù)責(zé)AppDelegate的某些功能。 最重要的是伪窖,window(窗口)的概念已被scene(場(chǎng)景)的概念所代替逸寓。 一個(gè)應(yīng)用程序可以具有不止一個(gè)場(chǎng)景,而一個(gè)場(chǎng)景現(xiàn)在可以作為您應(yīng)用程序的用戶界面和內(nèi)容的載體(背景)惰许。
尤其是一個(gè)具有多場(chǎng)景的App的概念很有趣席覆,因?yàn)樗鼓梢栽趇OS和iPadOS上構(gòu)建多窗口應(yīng)用程序。 例如汹买,文檔編輯器App中的每個(gè)文本文檔都可以有自己的場(chǎng)景佩伤。 用戶還可以創(chuàng)建場(chǎng)景的副本聊倔,同時(shí)運(yùn)行一個(gè)應(yīng)用程序的多個(gè)實(shí)例(類似多開(kāi))。
在Xcode 11中有三個(gè)地方可以明顯地看到SceneDelegate的身影:
- 現(xiàn)在生巡,一個(gè)新的iOS項(xiàng)目會(huì)自動(dòng)創(chuàng)建一個(gè)
SceneDelegate
類耙蔑,其中包括我們熟悉的生命周期事件,例如active孤荣,resign和disconnect甸陌。 - AppDelegate類中多了兩個(gè)與“scene sessions”相關(guān)的新方法:
application(_:configurationForConnecting:options:)
和application(_:didDiscardSceneSessions:)
- Info.plist文件中提供了”Application Scene Manifest“配置項(xiàng),用于配置App的場(chǎng)景盐股,包括它們的場(chǎng)景配置名钱豁,delegate類名和storyboard
讓我們一次開(kāi)看一看。
1. Scene Delegate Class
首先疯汁,SceneDelegate類:
SceneDelegate的最重要的函數(shù)是:scene(_:willConnectTo:options:)
牲尺。 在某種程度上,它與iOS 12上的 application(_:didFinishLaunchingWithOptions:)
函數(shù)的作用最相似幌蚊。當(dāng)將場(chǎng)景添加到app中時(shí)scene(_:willConnectTo:options:)
函數(shù)會(huì)被調(diào)用的谤碳,因此這里是配置場(chǎng)景的最理想地方。 在上面的代碼中溢豆,我們手動(dòng)地設(shè)置了視圖控制器堆棧蜒简,稍后會(huì)進(jìn)行詳細(xì)介紹。
這里需要特別注意的是漩仙,“SceneDelegate”采用了協(xié)議模式搓茬,并且這個(gè)delegate通常會(huì)響應(yīng)任何場(chǎng)景。 您使用一個(gè)Delegate來(lái)配置App中的所有場(chǎng)景讯赏。
SceneDelegate
還具有下面這些函數(shù):
-
sceneDidDisconnect(_:)
當(dāng)場(chǎng)景與app斷開(kāi)連接是調(diào)用(注意垮兑,以后它可能被重新連接) -
sceneDidBecomeActive(_:)
當(dāng)用戶開(kāi)始與場(chǎng)景進(jìn)行交互(例如從應(yīng)用切換器中選擇場(chǎng)景)時(shí),會(huì)調(diào)用 -
sceneWillResignActive(_:)
當(dāng)用戶停止與場(chǎng)景交互(例如通過(guò)切換器切換到另一個(gè)場(chǎng)景)時(shí)調(diào)用 -
sceneWillEnterForeground(_:)
當(dāng)場(chǎng)景變成活動(dòng)窗口時(shí)調(diào)用漱挎,即從后臺(tái)狀態(tài)變成開(kāi)始或恢復(fù)狀態(tài) -
sceneDidEnterBackground(_:)
當(dāng)場(chǎng)景進(jìn)入后臺(tái)時(shí)調(diào)用系枪,即該應(yīng)用已最小化但仍存活在后臺(tái)中
看到函數(shù)的對(duì)稱性了嗎? Active/inactive, background/foreground, 和 “disconnect”. 磕谅。 這些是任何應(yīng)用程序的典型生命周期事件私爷。
App Delegate中的Scene Sessions
在iOS13中AppDelegate中有兩個(gè)管理Senen Session的代理函數(shù)。在您的應(yīng)用創(chuàng)建scene(場(chǎng)景)后膊夹,“scene session”對(duì)象將跟蹤與該場(chǎng)景相關(guān)的所有信息衬浑。
這兩個(gè)函數(shù)是:
application(_:configurationForConnecting:options:)
, 會(huì)返回一個(gè)創(chuàng)建場(chǎng)景時(shí)需要的UISceneConfiguration對(duì)象application(_:didDiscardSceneSessions:)
, 當(dāng)用戶通過(guò)“應(yīng)用切換器”關(guān)閉一個(gè)或多個(gè)場(chǎng)景時(shí)會(huì)被調(diào)用
目前,SceneSession被用于指定場(chǎng)景放刨,例如“外部顯示” 或“ CarPlay” 工秩。 它還可用于還原場(chǎng)景的狀態(tài),如果您想使用【狀態(tài)還原】,SceneSession將非常有用助币。 狀態(tài)還原允許您在應(yīng)用啟動(dòng)之間保留并重新創(chuàng)建UI浪听。 您還可以將用戶信息存儲(chǔ)到場(chǎng)景會(huì)話中,它是一個(gè)可以放入任何內(nèi)容的字典眉菱。
application(_:didDiscardSceneSessions:)
很簡(jiǎn)單迹栓。 當(dāng)用戶通過(guò)“應(yīng)用程序切換器”關(guān)閉一個(gè)或多個(gè)場(chǎng)景時(shí),即會(huì)調(diào)用該方法俭缓。 您可以在該函數(shù)中銷毀場(chǎng)景所使用的資源克伊,因?yàn)椴粫?huì)再需要它們。
了解application(_:didDiscardSceneSessions:)
與sceneDidDisconnect(_ :)
的區(qū)別很重要华坦,后者僅在場(chǎng)景斷開(kāi)連接時(shí)調(diào)用愿吹,不會(huì)被丟棄,它可能會(huì)重新連接季春。而application(_:didDiscardSceneSessions:)
發(fā)生在使用【應(yīng)用程序切換器】退出場(chǎng)景時(shí)洗搂。
3. Info.plist 中的Application Scene Manifest
您的應(yīng)用支持的每個(gè)場(chǎng)景都需要在“Application Scene Manifest”(應(yīng)用場(chǎng)景清單)中聲明。 簡(jiǎn)而言之载弄,清單列出了您的應(yīng)用支持的每個(gè)場(chǎng)景。 大多數(shù)應(yīng)用程序只有一個(gè)場(chǎng)景撵颊,但是您可以創(chuàng)建更多場(chǎng)景宇攻,例如用于響應(yīng)推送通知或特定操作的特定場(chǎng)景。
Application Scene Manifest清單是Info.plist文件的一項(xiàng)倡勇,都知道該文件包含App的配置信息逞刷。 Info.plist包含諸如App的名稱,版本妻熊,支持的設(shè)備方向以及現(xiàn)在支持的不同場(chǎng)景等配置夸浅。
請(qǐng)務(wù)必注意,您聲明的是會(huì)話的“類型”扔役,而不是會(huì)話實(shí)例帆喇。 您的應(yīng)用程序可以支持一個(gè)場(chǎng)景,然后創(chuàng)建該場(chǎng)景的副本亿胸,來(lái)實(shí)現(xiàn)【多窗口】應(yīng)用程序坯钦。
下面看一下的 Info.plist
中清單的一些配置:
在紅框內(nèi),您會(huì)看到Application Scene Manifest 這一條侈玄。 在它下面一條是Enable Multiple Windows婉刀,需要將其設(shè)置為“ YES”以支持多個(gè)窗口。 再往下Application Session Role的值是一個(gè)數(shù)組序仙,用于在應(yīng)用程序中聲明場(chǎng)景突颊。 你也可以在數(shù)組中添加一條【外部屏幕】的場(chǎng)景聲明。
最重要的信息保存在Application Session Role數(shù)組中。 從中我們可以看到以下內(nèi)容:
- Configuration的名稱律秃,必須是唯一的
- 場(chǎng)景的代理類名呈昔,通常為
SceneDelegate
。 - 場(chǎng)景用于創(chuàng)建初始UI的storyboard名稱
Storyboard名稱這一項(xiàng)可能使您想起Main Interface設(shè)置友绝,該設(shè)置可以在Xcode 12項(xiàng)目的Project Properties配置中找到堤尾。 現(xiàn)在,在iOS應(yīng)用中迁客,你可以在此處設(shè)置或更改主Storyboard名稱郭宝。
AppDelegate中的SceneDelegate、UISceneSession和Application Scene Manifest是如何一起創(chuàng)建多窗口應(yīng)用的呢掷漱?
- 首先粘室,我們看
SceneDelegate
類。 它管理場(chǎng)景的生命周期卜范,處理各種響應(yīng)衔统,諸如sceneDidBecomeActive(_:)
andsceneDidEnterBackground(_:)
之類的事件。 - 然后海雪,我們?cè)倏纯?code>AppDelegate類中的新函數(shù)锦爵。 它管理場(chǎng)景會(huì)話(scene sessions),提供場(chǎng)景的配置數(shù)據(jù)奥裸,并響應(yīng)用戶丟棄場(chǎng)景的事件险掀。
- 最后,我們看了一下Application Scene Manifest湾宙。 它列出了您的應(yīng)用程序支持的場(chǎng)景樟氢,并將它們連接到delegate類并初始化storyboard。
Awesome! Now that we’ve got a playing field, let’s find out how scenes affects building UIs in Xcode 11.
太棒了侠鳄! 現(xiàn)在讓我們了解scenes(場(chǎng)景)是如何影響Xcode 11中的用戶界面的吧埠啃。
在SwiftUI中使用Scene Delegate
不久將來(lái),SwiftUI將是創(chuàng)建iOS 13項(xiàng)目最簡(jiǎn)單的方法伟恶。 簡(jiǎn)言之碴开,SwiftUI應(yīng)用程序主要依靠SceneDelegate來(lái)設(shè)置應(yīng)用程序的初始UI。
首先知押,SwiftUI項(xiàng)目中“Application Scene Manifest ”將長(zhǎng)這樣:
特別注意叹螟,配置中沒(méi)有設(shè)置“Storyboard Name”這一項(xiàng)。 請(qǐng)記住台盯,如果要支持多個(gè)窗口罢绽,則需要將Enable Multiple Windows設(shè)置為YES
。
我們將跳過(guò)“ AppDelegate”静盅,因?yàn)樗喈?dāng)標(biāo)準(zhǔn)良价。在SwiftUI項(xiàng)目中寝殴,只會(huì)返回“true”。
接下來(lái)是SceneDelegate
類明垢。 正如我們之前討論的蚣常,SceneDelegate負(fù)責(zé)設(shè)置您應(yīng)用中的場(chǎng)景,以及設(shè)置首個(gè)頁(yè)面痊银。
像下面一樣:
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()
}
}
}
上面的代碼中發(fā)生了什么抵蚊?
- 首先,必須明確的是 在將新場(chǎng)景添加到應(yīng)用中后 會(huì)調(diào)用
scene(_:willConnectTo:options:)
代理函數(shù)溯革。 它提供了一個(gè)scene
對(duì)象(和一個(gè)session)贞绳。 這個(gè)“UIWindowScene”對(duì)象是由應(yīng)用創(chuàng)建的,您無(wú)需進(jìn)行其他操作致稀。 - 其次冈闭,
window
屬性會(huì)在這里用到。 App仍然使用“ UIWindow”對(duì)象抖单,但現(xiàn)在它們已成為scene(場(chǎng)景)的一部分萎攒。 在if let
代碼塊中,您可以清楚地看到如何使用scene來(lái)初始化UIWindow對(duì)象的矛绘。 - 然后是設(shè)置window的rootViewController耍休,將
window
實(shí)例分配給了場(chǎng)景的window
屬性,并且設(shè)置窗口makeKeyAndVisible
為true蔑歌,即將該窗口置于App的前面羹应。 - 接著為SwiftUI項(xiàng)目創(chuàng)建了ContentView實(shí)例,并通過(guò)使用UIHostingController將其添加為根視圖控制器次屠。 該控制器用于將基于SwiftUI的視圖顯示在屏幕上。
- 最后但并非不重要的一點(diǎn)雳刺,值得注意的是劫灶,UIScene的實(shí)例化對(duì)象scene實(shí)際上是UIWindowScene類型的對(duì)象。 這就是
as?
對(duì)可選類型轉(zhuǎn)換的原因掖桦。 (到目前為止本昏,已創(chuàng)建的場(chǎng)景通常為“ UIWindowScene”類型,但我猜想將來(lái)還會(huì)看到更多類型的場(chǎng)景枪汪。)
所有這些看起來(lái)似乎很復(fù)雜涌穆,但是從高層次的概述來(lái)看,這很簡(jiǎn)單:
- 當(dāng)
scene(_:willConnectTo:options:)
被調(diào)用時(shí)雀久,SceneDelegate會(huì)在正確的時(shí)間配置場(chǎng)景宿稀。 - AppDelegate和Manifest的默認(rèn)配置,他們沒(méi)有涉及storyboard的任何東西赖捌。
-
scene(_:willConnectTo:options :)
函數(shù)內(nèi)祝沸,創(chuàng)建一個(gè)SwiftUI視圖,將其放置在托管控制器中,然后將控制器分配給window屬性的根視圖控制器罩锐,并將該窗口放置在應(yīng)用程序UI的前面 奉狈。
太棒了! 讓我們繼續(xù)涩惑。
您可以通過(guò)選擇File(文件)→New(新建)→Project(項(xiàng)目)來(lái)建立一個(gè)基本的Xcode 11項(xiàng)目仁期。 然后,選擇
Single View App
, 在User Interface
處選擇SwiftUI來(lái)創(chuàng)建一個(gè)SwiftUI項(xiàng)目
在Storyboards項(xiàng)目中使用SceneDelegate
Storyboards和XIB是為iOS應(yīng)用程序構(gòu)建UI的有效方法竭恬。 在iOS 13上也是如此跛蛋。 在將來(lái),我們將看到更多的SwiftUI應(yīng)用萍聊,但目前问芬,Storyboards更常見(jiàn)。
有趣的是寿桨,即使有了SceneDelegate此衅,通過(guò)Storyboards創(chuàng)建iOS項(xiàng)目你也不需要做任何額外的事情 只需選擇File → New → Project
。 然后亭螟,選擇Single View App
挡鞍。 最后,為 User Interface 處選擇 Storyboard 预烙,就完成了墨微。
設(shè)置方法如下:
- 如我們前面提到過(guò)的,您可以在Info.plist中的“ Application Scene Manifest”中找到
Main
storyboard的設(shè)置地方扁掸。 - 默認(rèn)情況下翘县,AppDelegate將使用默認(rèn)的UISceneConfiguration。
- SceneDelegate會(huì)設(shè)置一個(gè)“UIWindow”對(duì)象谴分,并使用“Main.storyboard”來(lái)創(chuàng)建初始UI锈麸。
純代碼編寫(xiě)UI
許多開(kāi)發(fā)人員喜歡手寫(xiě)UI,而隨著SwiftUI的興起牺蹄,使用SwiftUI手寫(xiě)代碼將會(huì)越來(lái)越常見(jiàn)忘伞。 如果您不使用storyboards,而使用XIB創(chuàng)建應(yīng)用程序UI沙兰,該怎么辦氓奈? 讓我們看看SceneDelegate
如何修改。
首先鼎天,AppDelegate和Application Scene Manifest中保持默認(rèn)值舀奶。 我們不使用storyboard,所以需要在SceneDelegate類的scene(_:willConnectTo:options:)
函數(shù)中設(shè)置初始視圖控制器训措。
像下面這樣:
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()
}
}
}
上面代碼很簡(jiǎn)單我們就不詳細(xì)介紹了伪节。
很簡(jiǎn)單光羞,對(duì)吧? 使用SceneDelegate的核心是將一些代碼從AppDelegate移至到SceneDelegate中怀大,并正確配置 Application Scene Manifest 纱兑。
想要為現(xiàn)有的iOS項(xiàng)目添加scene(場(chǎng)景)支持? 可以查看Apple官方文檔https://developer.apple.com/documentation/uikit/app_and_environment/scenes/specifying_the_scenes_your_app_supports化借。
作者:Reinder de Vries
翻譯:樂(lè)Coding