iOS13 Scene Delegate詳解

image

iOS13 項(xiàng)目中的SceneDelegate類有什么作用?自從Xcode11發(fā)布以來,當(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)目。

通過閱讀本文你將了解到:

  • SceneDelegate和AppDelegate的新變化
  • 他們是如何合作引導(dǎo)你的app啟動(dòng)的
  • 在純手寫App中使用SceneDelegate
  • 在Storyboards 和 SwiftUI項(xiàng)目中使用SceneDelegate

讓我們開始吧。

本篇文章基于 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庫中的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很簡單。 像這樣:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
{
    return true
}

一個(gè)使用XIB的簡單應(yīng)用看起來像這樣:

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è)主要背景層來顯示您的應(yīng)用內(nèi)容。 從某種意義上說,“ Windows”的概念就是微軟定義的窗口,而在iOS上,這個(gè)概念沒有什么不同。 (謝謝,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(場景)的概念所代替。 一個(gè)應(yīng)用程序可以具有不止一個(gè)場景,而一個(gè)場景現(xiàn)在可以作為您應(yīng)用程序的用戶界面和內(nèi)容的載體(背景)。

尤其是一個(gè)具有多場景的App的概念很有趣,因?yàn)樗鼓梢栽趇OS和iPadOS上構(gòu)建多窗口應(yīng)用程序。 例如,文檔編輯器App中的每個(gè)文本文檔都可以有自己的場景。 用戶還可以創(chuàng)建場景的副本,同時(shí)運(yùn)行一個(gè)應(yīng)用程序的多個(gè)實(shí)例(類似多開)。

在Xcode 11中有三個(gè)地方可以明顯地看到SceneDelegate的身影:

  1. 現(xiàn)在,一個(gè)新的iOS項(xiàng)目會(huì)自動(dòng)創(chuàng)建一個(gè)SceneDelegate類,其中包括我們熟悉的生命周期事件,例如active,resign和disconnect。
  2. AppDelegate類中多了兩個(gè)與“scene sessions”相關(guān)的新方法:application(_:configurationForConnecting:options:)application(_:didDiscardSceneSessions:)
  3. Info.plist文件中提供了”Application Scene Manifest“配置項(xiàng),用于配置App的場景,包括它們的場景配置名,delegate類名和storyboard

讓我們一次開看一看。

1. Scene Delegate Class

首先,SceneDelegate類:

image

SceneDelegate的最重要的函數(shù)是:scene(_:willConnectTo:options:)。 在某種程度上,它與iOS 12上的 application(_:didFinishLaunchingWithOptions:) 函數(shù)的作用最相似。當(dāng)將場景添加到app中時(shí)scene(_:willConnectTo:options:)函數(shù)會(huì)被調(diào)用的,因此這里是配置場景的最理想地方。 在上面的代碼中,我們手動(dòng)地設(shè)置了視圖控制器堆棧,稍后會(huì)進(jìn)行詳細(xì)介紹。

這里需要特別注意的是,“SceneDelegate”采用了協(xié)議模式,并且這個(gè)delegate通常會(huì)響應(yīng)任何場景。 您使用一個(gè)Delegate來配置App中的所有場景。

SceneDelegate 還具有下面這些函數(shù):

  • sceneDidDisconnect(_:) 當(dāng)場景與app斷開連接是調(diào)用(注意,以后它可能被重新連接)
  • sceneDidBecomeActive(_:) 當(dāng)用戶開始與場景進(jìn)行交互(例如從應(yīng)用切換器中選擇場景)時(shí),會(huì)調(diào)用
  • sceneWillResignActive(_:) 當(dāng)用戶停止與場景交互(例如通過切換器切換到另一個(gè)場景)時(shí)調(diào)用
  • sceneWillEnterForeground(_:) 當(dāng)場景變成活動(dòng)窗口時(shí)調(diào)用,即從后臺(tái)狀態(tài)變成開始或恢復(fù)狀態(tài)
  • sceneDidEnterBackground(_:) 當(dā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(場景)后,“scene session”對(duì)象將跟蹤與該場景相關(guān)的所有信息。

這兩個(gè)函數(shù)是:

  • application(_:configurationForConnecting:options:), 會(huì)返回一個(gè)創(chuàng)建場景時(shí)需要的UISceneConfiguration對(duì)象

  • application(_:didDiscardSceneSessions:), 當(dāng)用戶通過“應(yīng)用切換器”關(guān)閉一個(gè)或多個(gè)場景時(shí)會(huì)被調(diào)用

目前,SceneSession被用于指定場景,例如“外部顯示” 或“ CarPlay” 。 它還可用于還原場景的狀態(tài),如果您想使用【狀態(tài)還原】,SceneSession將非常有用。 狀態(tài)還原允許您在應(yīng)用啟動(dòng)之間保留并重新創(chuàng)建UI。 您還可以將用戶信息存儲(chǔ)到場景會(huì)話中,它是一個(gè)可以放入任何內(nèi)容的字典。

application(_:didDiscardSceneSessions:)很簡單。 當(dāng)用戶通過“應(yīng)用程序切換器”關(guān)閉一個(gè)或多個(gè)場景時(shí),即會(huì)調(diào)用該方法。 您可以在該函數(shù)中銷毀場景所使用的資源,因?yàn)椴粫?huì)再需要它們。

了解application(_:didDiscardSceneSessions:)sceneDidDisconnect(_ :)的區(qū)別很重要,后者僅在場景斷開連接時(shí)調(diào)用,不會(huì)被丟棄,它可能會(huì)重新連接。而application(_:didDiscardSceneSessions:)發(fā)生在使用【應(yīng)用程序切換器】退出場景時(shí)。

3. Info.plist 中的Application Scene Manifest

您的應(yīng)用支持的每個(gè)場景都需要在“Application Scene Manifest”(應(yīng)用場景清單)中聲明。 簡而言之,清單列出了您的應(yīng)用支持的每個(gè)場景。 大多數(shù)應(yīng)用程序只有一個(gè)場景,但是您可以創(chuàng)建更多場景,例如用于響應(yīng)推送通知或特定操作的特定場景。

Application Scene Manifest清單是Info.plist文件的一項(xiàng),都知道該文件包含App的配置信息。 Info.plist包含諸如App的名稱,版本,支持的設(shè)備方向以及現(xiàn)在支持的不同場景等配置。

請(qǐng)務(wù)必注意,您聲明的是會(huì)話的“類型”,而不是會(huì)話實(shí)例。 您的應(yīng)用程序可以支持一個(gè)場景,然后創(chuàng)建該場景的副本,來實(shí)現(xiàn)【多窗口】應(yīng)用程序。

下面看一下的 Info.plist中清單的一些配置:

image

在紅框內(nèi),您會(huì)看到Application Scene Manifest 這一條。 在它下面一條是Enable Multiple Windows,需要將其設(shè)置為“ YES”以支持多個(gè)窗口。 再往下Application Session Role的值是一個(gè)數(shù)組,用于在應(yīng)用程序中聲明場景。 你也可以在數(shù)組中添加一條【外部屏幕】的場景聲明。

最重要的信息保存在Application Session Role數(shù)組中。 從中我們可以看到以下內(nèi)容:

  • Configuration的名稱,必須是唯一的
  • 場景的代理類名,通常為SceneDelegate
  • 場景用于創(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類。 它管理場景的生命周期,處理各種響應(yīng),諸如 sceneDidBecomeActive(_:) and sceneDidEnterBackground(_:)之類的事件。
  • 然后,我們?cè)倏纯?code>AppDelegate類中的新函數(shù)。 它管理場景會(huì)話(scene sessions),提供場景的配置數(shù)據(jù),并響應(yīng)用戶丟棄場景的事件。
  • 最后,我們看了一下Application Scene Manifest。 它列出了您的應(yī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(場景)是如何影響Xcode 11中的用戶界面的吧。

在SwiftUI中使用Scene Delegate

不久將來,SwiftUI將是創(chuàng)建iOS 13項(xiàng)目最簡單的方法。 簡言之,SwiftUI應(yīng)用程序主要依靠SceneDelegate來設(shè)置應(yīng)用程序的初始UI。

首先,SwiftUI項(xiàng)目中“Application Scene Manifest ”將長這樣:

image

特別注意,配置中沒有設(shè)置“Storyboard Name”這一項(xiàng)。 請(qǐng)記住,如果要支持多個(gè)窗口,則需要將Enable Multiple Windows設(shè)置為YES

我們將跳過“ AppDelegate”,因?yàn)樗喈?dāng)標(biāo)準(zhǔn)。在SwiftUI項(xiàng)目中,只會(huì)返回“true”。

接下來是SceneDelegate類。 正如我們之前討論的,SceneDelegate負(fù)責(zé)設(shè)置您應(yīng)用中的場景,以及設(shè)置首個(gè)頁面。

像下面一樣:

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ā)生了什么?

  • 首先,必須明確的是 在將新場景添加到應(yīng)用中后 會(huì)調(diào)用scene(_:willConnectTo:options:)代理函數(shù)。 它提供了一個(gè)scene對(duì)象(和一個(gè)session)。 這個(gè)“UIWindowScene”對(duì)象是由應(yīng)用創(chuàng)建的,您無需進(jìn)行其他操作。
  • 其次,window屬性會(huì)在這里用到。 App仍然使用“ UIWindow”對(duì)象,但現(xiàn)在它們已成為scene(場景)的一部分。 在if let代碼塊中,您可以清楚地看到如何使用scene來初始化UIWindow對(duì)象的。
  • 然后是設(shè)置window的rootViewController,將window實(shí)例分配給了場景的window屬性,并且設(shè)置窗口makeKeyAndVisible為true,即將該窗口置于App的前面。
  • 接著為SwiftUI項(xiàng)目創(chuàng)建了ContentView實(shí)例,并通過使用UIHostingController將其添加為根視圖控制器。 該控制器用于將基于SwiftUI的視圖顯示在屏幕上。
  • 最后但并非不重要的一點(diǎn),值得注意的是,UIScene的實(shí)例化對(duì)象scene實(shí)際上是UIWindowScene類型的對(duì)象。 這就是as?對(duì)可選類型轉(zhuǎn)換的原因。 (到目前為止,已創(chuàng)建的場景通常為“ UIWindowScene”類型,但我猜想將來還會(huì)看到更多類型的場景。)

所有這些看起來似乎很復(fù)雜,但是從高層次的概述來看,這很簡單:

  • 當(dāng)scene(_:willConnectTo:options:)被調(diào)用時(shí),SceneDelegate會(huì)在正確的時(shí)間配置場景。
  • AppDelegate和Manifest的默認(rèn)配置,他們沒有涉及storyboard的任何東西。
  • scene(_:willConnectTo:options :)函數(shù)內(nèi),創(chuàng)建一個(gè)SwiftUI視圖,將其放置在托管控制器中,然后將控制器分配給window屬性的根視圖控制器,并將該窗口放置在應(yīng)用程序UI的前面 。

太棒了! 讓我們繼續(xù)。

您可以通過選擇File(文件)→New(新建)→Project(項(xiàng)目)來建立一個(gè)基本的Xcode 11項(xiàng)目。 然后,選擇Single View App, 在User Interface處選擇SwiftUI來創(chuàng)建一個(gè)SwiftUI項(xiàng)目

在Storyboards項(xiàng)目中使用SceneDelegate

Storyboards和XIB是為iOS應(yīng)用程序構(gòu)建UI的有效方法。 在iOS 13上也是如此。 在將來,我們將看到更多的SwiftUI應(yīng)用,但目前,Storyboards更常見。

有趣的是,即使有了SceneDelegate,通過Storyboards創(chuàng)建iOS項(xiàng)目你也不需要做任何額外的事情 只需選擇File → New → Project。 然后,選擇Single View App。 最后,為 User Interface 處選擇 Storyboard ,就完成了。

設(shè)置方法如下:

  • 如我們前面提到過的,您可以在Info.plist中的“ Application Scene Manifest”中找到Main storyboard的設(shè)置地方。
  • 默認(rèn)情況下,AppDelegate將使用默認(rèn)的UISceneConfiguration。
  • SceneDelegate會(huì)設(shè)置一個(gè)“UIWindow”對(duì)象,并使用“Main.storyboard”來創(chuàng)建初始UI。

純代碼編寫UI

許多開發(fā)人員喜歡手寫UI,而隨著SwiftUI的興起,使用SwiftUI手寫代碼將會(huì)越來越常見。 如果您不使用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()
        }
    }
}

上面代碼很簡單我們就不詳細(xì)介紹了。

很簡單,對(duì)吧? 使用SceneDelegate的核心是將一些代碼從AppDelegate移至到SceneDelegate中,并正確配置 Application Scene Manifest 。

鏈接:http://www.lxweimin.com/p/53e3252dc07e
來源:簡書

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容