1包券、導讀
iOS13 項目中的SceneDelegate類有什么作用?自從Xcode11發(fā)布以來,當你使用新XCode創(chuàng)建一個新的iOS項目時世吨,SceneDelegate會被默認創(chuàng)建户魏,它到底有什么用呢兽肤。
在本文中套腹,我們將深入探討iOS 13和Xcode 11的一些變化。我們將重點關注SceneDelegate和AppDelegate资铡,以及它們?nèi)绾斡绊慡wiftUI电禀、Storyboard和基于XIB的UI項目。
通過閱讀本文你將了解到:
- SceneDelegate和AppDelegate的新變化
- 他們是如何合作引導你的app啟動的
- 在純手寫App中使用SceneDelegate
- 在Storyboards 和 SwiftUI項目中使用SceneDelegate
- 不是用SceneDelegate的話怎么處理
2笤休、AppDelegate回顧
你可能對AppDelegate已經(jīng)熟悉尖飞,他是iOS app的入口,{application(_:didFinishLaunchingWithOptions:)}
是你的app啟動后系統(tǒng)調(diào)用的第一個函數(shù)店雅。
AppDelegate類實現(xiàn)了UIKit庫中的UIApplicationDelegate 協(xié)議政基。而到了iOS13 AppDelegate的角色將會發(fā)生變化,后面我們會詳細討論闹啦。
下面是你在iOS12中一般會在AppDelegate中做的事情:
- 創(chuàng)建app的第一個view controller也就是 rootViewController
- 配置并啟動一些像日志記錄和云服務之類的組件
- 注冊推送通知處理程序沮明,并響應發(fā)送到app的推送通知
- 響應應用程序生命周期事件,例如進入后臺窍奋,恢復應用程序或退出應用程序(終止)
//iOS12及以前荐健,使用Storyboards的app,AppDelegate很簡單琳袄。 像這樣:
func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool{
return true
}
//一個使用XIB的簡單應用看起來像這樣:
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: Framew
window!.rootViewController = navigation
window!.makeKeyAndVisible()
return true
}
在上面的代碼中江场,我們創(chuàng)建一個ViewController,并將其放在navigation controller中。然后將其分配給UIWindow對象的rootViewController屬性窖逗。 這個window對象是AppDelegate的屬性址否,它是我們的應用的一個窗口。
應用程序的window是一個重要的概念碎紊。 本質(zhì)上佑附,窗口就是應用程序,大多數(shù)iOS應用程序只有一個窗口仗考。 它包含您應用的用戶界面(UI)音同,將事件調(diào)度到視圖,并提供了一個主要背景層來顯示您的應用內(nèi)容痴鳄。 從某種意義上說瘟斜,“ Windows”的概念就是微軟定義的窗口,而在iOS上痪寻,這個概念沒有什么不同螺句。
如果“窗口”的概念仍然不了解,請查看iPhone上的應用程序切換器橡类。 雙擊Home鍵或從iPhone底部向上滑動蛇尚,然后您會看到當前正在運行的應用程序的窗口。 這就是應用程序切換器顾画。
3取劫、SceneDelegate使用
在iOS 13(及以后版本)上匆笤,SceneDelegate將負責AppDelegate的某些功能。 最重要的是谱邪,window(窗口)的概念已被scene(場景)的概念所代替炮捧。 一個應用程序可以具有不止一個場景,而一個場景現(xiàn)在可以作為您應用程序的用戶界面和內(nèi)容的載體(背景)惦银。
尤其是一個具有多場景的App的概念很有趣咆课,因為它使您可以在iOS和iPadOS上構建多窗口應用程序。 例如扯俱,文檔編輯器App中的每個文本文檔都可以有自己的場景书蚪。 用戶還可以創(chuàng)建場景的副本,同時運行一個應用程序的多個實例(類似多開)迅栅。
3.1 SceneDelegate相關文件介紹
在Xcode 11中有三個地方可以明顯地看到SceneDelegate的身影:
- 現(xiàn)在殊校,一個新的iOS項目會自動創(chuàng)建一個SceneDelegate類,其中包括我們熟悉的生命周期事件读存,例如active为流,resign和disconnect。
- AppDelegate類中多了兩個與“scene sessions”相關的新方法:
application(_:configurationForConnecting:options:)
和application(_:didDiscardSceneSessions:)
- Info.plist文件中提供了”Application Scene Manifest“配置項宪萄,用于配置App的場景艺谆,包括它們的場景配置名榨惰,delegate類名和storyboard
3.1.1 SceneDelegate類
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
//1拜英、SceneDelegate的最重要的函數(shù)是:`scene(_:willConnectTo:options:)`。
// 在某種程度上琅催,它與iOS 12上的 `application(_:didFinishLaunchingWithOptions:)` 函數(shù)的作用最相似居凶。
// 當將場景添加到app中時`scene(_:willConnectTo:options:)`函數(shù)會被調(diào)用的,因此這里是配置場景的最理想地方藤抡。
//2侠碧、這里需要特別注意的是,“SceneDelegate”采用了協(xié)議模式缠黍,并且這個delegate通常會響應任何場景弄兜。
// 使用一個Delegate來配置App中的所有場景。
//手動地設置視圖控制器堆棧
if let windowScene = scene as? UIWindowScene {
window = UIWindow(windowScene: windowScene)
let vc = ViewController()
let navigation = UINavigationController(rootViewController: vc)
window.rootViewController = navigation
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
// 當場景與app斷開連接是調(diào)用(注意瓷式,以后它可能被重新連接)
}
func sceneDidBecomeActive(_ scene: UIScene) {
// 當用戶開始與場景進行交互(例如從應用切換器中選擇場景)時替饿,會調(diào)用
}
func sceneWillResignActive(_ scene: UIScene) {
// 當用戶停止與場景交互(例如通過切換器切換到另一個場景)時調(diào)用
}
func sceneWillEnterForeground(_ scene: UIScene) {
// 當場景變成活動窗口時調(diào)用,即從后臺狀態(tài)變成開始或恢復狀態(tài)
}
func sceneDidEnterBackground(_ scene: UIScene) {
// 當場景進入后臺時調(diào)用贸典,即該應用已最小化但仍存活在后臺中
}
}
3.1.2 AppDelegate 中的 SceneSessions
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// MARK: UISceneSession Lifecycle
//在iOS13中AppDelegate中有兩個管理Senen Session的代理函數(shù)视卢。
//在您的應用創(chuàng)建scene(場景)后,“scene session”對象將跟蹤與該場景相關的所有信息廊驼。
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// 會返回一個創(chuàng)建場景時需要的UISceneConfiguration對象
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// 當用戶通過“應用切換器”關閉一個或多個場景時會被調(diào)用
// 您可以在該函數(shù)中銷毀場景所使用的資源据过,因為不會再需要它們惋砂。
// 了解application(_:didDiscardSceneSessions:)與sceneDidDisconnect(_ :)的區(qū)別很重要,后者僅在場景斷開連接時調(diào)用绳锅,不會被丟棄西饵,它可能會重新連接。
// 而application(_:didDiscardSceneSessions:)發(fā)生在使用【應用程序切換器】退出場景時鳞芙。
}
}
目前罗标,SceneSession被用于指定場景,例如“外部顯示” 或“ CarPlay” 积蜻。 它還可用于還原場景的狀態(tài)闯割,如果您想使用【狀態(tài)還原】,SceneSession將非常有用竿拆。 狀態(tài)還原允許您在應用啟動之間保留并重新創(chuàng)建UI宙拉。 您還可以將用戶信息存儲到場景會話中,它是一個可以放入任何內(nèi)容的字典丙笋。
3.1.3 Info.plist 中的Application Scene Manifest
您的應用支持的每個場景都需要在“Application Scene Manifest”(應用場景清單)中聲明谢澈。 簡而言之,清單列出了您的應用支持的每個場景御板。 大多數(shù)應用程序只有一個場景锥忿,但是您可以創(chuàng)建更多場景,例如用于響應推送通知或特定操作的特定場景怠肋。
Application Scene Manifest清單是Info.plist文件的一項敬鬓,都知道該文件包含App的配置信息。 Info.plist包含諸如App的名稱笙各,版本钉答,支持的設備方向以及現(xiàn)在支持的不同場景等配置。
請務必注意杈抢,您聲明的是會話的“類型”数尿,而不是會話實例。 您的應用程序可以支持一個場景惶楼,然后創(chuàng)建該場景的副本右蹦,來實現(xiàn)【多窗口】應用程序。
在上圖中歼捐,您會看到Application Scene Manifest 這一條何陆。 在它下面一條是Enable Multiple Windows,需要將其設置為“ YES”以支持多個窗口窥岩。 再往下Application Session Role的值是一個數(shù)組甲献,用于在應用程序中聲明場景。 你也可以在數(shù)組中添加一條【外部屏幕】的場景聲明颂翼。
最重要的信息保存在Application Session Role數(shù)組中晃洒。 從中我們可以看到以下內(nèi)容:
- Configuration的名稱慨灭,必須是唯一的
- 場景的代理類名,通常為SceneDelegate球及。
- 場景用于創(chuàng)建初始UI的storyboard名稱
Storyboard名稱這一項可能使您想起Main Interface設置氧骤,該設置可以在Xcode 12項目的Project Properties配置中找到。 現(xiàn)在吃引,在iOS應用中筹陵,你可以在此處設置或更改主Storyboard名稱。
AppDelegate中的SceneDelegate镊尺、UISceneSession和Application Scene Manifest是如何一起創(chuàng)建多窗口應用的呢朦佩?
- 首先,我們看SceneDelegate類庐氮。 它管理場景的生命周期语稠,處理各種響應,諸如
sceneDidBecomeActive(_:)
和sceneDidEnterBackground(_:)
之類的事件弄砍。 - 然后扔傅,我們再看看AppDelegate類中的新函數(shù)蒜撮。 它管理場景會話(scene sessions),提供場景的配置數(shù)據(jù)先紫,并響應用戶丟棄場景的事件倘屹。
- 最后盐肃,我們看了一下Application Scene Manifest帚屉。 它列出了您的應用程序支持的場景渤愁,并將它們連接到delegate類并初始化storyboard。
3.2 在SwiftUI中使用Scene Delegate
不久將來瞳收,SwiftUI將是創(chuàng)建iOS項目最簡單的方法碉京。 簡言之厢汹,SwiftUI應用程序主要依靠SceneDelegate來設置應用程序的初始UI螟深。
3.2.1 配置
- Application Scene Manifest
~ 特別注意,配置中沒有設置“Storyboard Name”這一項烫葬。 請記住界弧,如果要支持多個窗口,則需要將Enable Multiple Windows設置為YES ~
AppDelegate
我們將跳過“ AppDelegate”搭综,因為它相當標準垢箕。在SwiftUI項目中,只會返回“true”兑巾。SceneDelegate
正如我們之前討論的条获,SceneDelegate負責設置您應用中的場景,以及設置首個頁面蒋歌。
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ā)生了什么帅掘?
1委煤、首先,必須明確的是 在將新場景添加到應用中后 會調(diào)用 'scene(_:willConnectTo:options:) '代理函數(shù)修档。
它提供了一個'scene'對象和一個'session'碧绞。 這個'UIWindowScene'對象是由應用創(chuàng)建的,您無需進行其他操作吱窝。
2讥邻、其次,'window'屬性會在這里用到院峡。 App仍然使用'UIWindow'對象兴使,但現(xiàn)在它們已成為'scene'(場景)的一部分。
在'if let'代碼塊中照激,您可以清楚地看到如何使用'scene'來初始化'UIWindow'對象的鲫惶。
3、然后是設置'window'的'rootViewController'实抡,將'window'實例分配給了場景的'window'屬性欠母,并且設置窗口'makeKeyAndVisible'為true,即將該窗口置于App的前面吆寨。
接著為SwiftUI項目創(chuàng)建了'ContentView'實例赏淌,并通過使用'UIHostingController'將其添加為根視圖控制器。
該控制器用于將基于SwiftUI的視圖顯示在屏幕上啄清。
4六水、最后但并非不重要的一點,值得注意的是辣卒,'UIScene'的實例化對象'scene'實際上是'UIWindowScene'類型的對象掷贾。這就是'as?'對可選類型轉(zhuǎn)換的原因。
所有這些看起來似乎很復雜荣茫,但是從高層次的概述來看想帅,這很簡單:
當'scene(_:willConnectTo:options:)'被調(diào)用時,'SceneDelegate'會在正確的時間配置場景啡莉。
'AppDelegate'和'Manifest'的默認配置港准,他們沒有涉及'storyboard'的任何東西。
'scene(_:willConnectTo:options :)'函數(shù)內(nèi)咧欣,創(chuàng)建一個SwiftUI視圖浅缸,將其放置在托管控制器中,
然后將控制器分配給'window'屬性的根視圖控制器魄咕,并將該窗口放置在應用程序UI的前面 衩椒。
您可以通過選擇File(文件)→New(新建)→Project(項目)來建立一個基本的Xcode 11項目。 然后,選擇Single View App, 在User Interface處選擇SwiftUI來創(chuàng)建一個SwiftUI項目
3.3 在Storyboards項目中使用SceneDelegate
Storyboards和XIB是為iOS應用程序構建UI的有效方法毛萌。 在iOS 13梢什、14也是如此。 在將來朝聋,我們將看到更多的SwiftUI應用嗡午,但目前,Storyboards更常見冀痕。
有趣的是荔睹,即使有了SceneDelegate,通過Storyboards創(chuàng)建iOS項目你也不需要做任何額外的事情 只需選擇File → New → Project言蛇。 然后僻他,選擇Single View App。 最后腊尚,為 User Interface 處選擇 Storyboard 吨拗,就完成了。
設置方法如下:
- 如我們前面提到過的婿斥,您可以在Info.plist中的
Application Scene Manifest
中找到Main storyboard的設置地方劝篷。 - 默認情況下,AppDelegate將使用默認的UISceneConfiguration民宿。
- SceneDelegate會設置一個
UIWindow
對象娇妓,并使用Main.storyboard
來創(chuàng)建初始UI。
3.3 純代碼編寫UI
許多開發(fā)人員喜歡手寫UI活鹰,而隨著SwiftUI的興起哈恰,使用SwiftUI手寫代碼將會越來越常見。 如果您不使用storyboards志群,而使用XIB創(chuàng)建應用程序UI着绷,該怎么辦?
首先锌云,AppDelegate
和Application Scene Manifest
中保持默認值荠医。
我們不使用storyboard,所以需要在SceneDelegate
類的scene(_:willConnectTo:options:)
函數(shù)中設置初始視圖控制器宾抓。
然后子漩,刪除info.plist中的Main storyboard file base name
條目和Application Scene Manifest
下的Storyboard Name
條目,Main.storyboard
文件留與不留看你心意石洗。
大功告成。
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()
}
}
}
4紧显、不使用SceneDelegate
參考文章: