SceneDelegate是為了支持 iOS13之后的 iPadOS 多窗口口而退出的。Xcode11 默認會創(chuàng)建通過
UIScene管理多個 UIWindow 的應(yīng)用,工程中除了 AppDelegate 外會多一個 SceneDelegate。并且 AppDelegate.h 不再有 window 屬性,window 屬性被定義在了 SceneDelegate.h 中,SceneDelegate 負責原 AppDelegate 的 UI 生命周期部分的職責。
-
iOS13之前
AppDelegate的職責全權(quán)處理 App 生命周期和 UI 的生命周期。
image.png
這種模式完全沒有問題,因為只有一個進程,只有一個與這個進程對應(yīng)的用戶界面。
- iOS13 之后
AppDelegate 的職責是處理 App 的生命周期;
新增的 SceneDelegate 是處理 UI 的生命周期。


- Xcode11 新建項目的 AppDelegate 文件
import UIKit
@UIApplicationMain
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
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
}
}
- Xcode11 新建項目的 SceneDelegate 文件
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}
對于這個特性的適配
- 方案 1:不需要多窗口(multiple windows)
如果需要支持 iOS13 及之前多個版本的 iOS,并且又不需要多個窗口的功能,可以直接刪除以下內(nèi)容:
1.info.plist文件中的Application Scene Manifest的配置數(shù)據(jù)
2.AppDelegate 中關(guān)于 Scene 的代理方法
3.SceneDelegate 的類
4.實現(xiàn)UIApplicationDelegate的方法
如果使用純代碼來實現(xiàn)顯示界面,需要在 AppDelegate中手動添加 window 屬性:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds);
window?.backgroundColor = .white
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
return true
}
}
- 方案 2:需要多窗口
不在 AppDelegate 的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中初始化 window 了,因為 AppDelegate 中也沒有這個屬性了,轉(zhuǎn)交給 SceneDelegate 的 willConnectToSession: 方法進行根控制器設(shè)置:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: scene as! UIWindowScene)
self.window?.frame = UIScreen.main.bounds
self.window?.backgroundColor = .white
self.window?.rootViewController = ViewController1()
self.window?.makeKeyAndVisible()
}
下面是關(guān)于UIScene 以及 SceneDelegate 的一些介紹了。
UIScene 的介紹
表示應(yīng)用程序用戶界面實例的對象。
描述:
1.UIKit 為用戶或應(yīng)用程序請求的每個應(yīng)用的 UI 實例創(chuàng)建一個場景對象(也就是一個 UIScene),也就是UIWindowScene(繼承于 UIScene) 對象。
2.UIScene 會有一個代理對象(實現(xiàn) UISceneDelegate),用以接收 UIScene 的狀態(tài)改變,例如,使用它來確定你的場景何時移動到背景。
3.通過調(diào)用
UIApplication的requestSceneSessionActivation:userActivity:options:errorHandler:方法去創(chuàng)建一個 scene。UIkit 還根據(jù)用戶交互創(chuàng)建場景。請注意,我們使用的都是 UIWindowScene ,而不是 UIScene。
方法列表
- 創(chuàng)建一個實例
** 通過制定的UISceneSession對象和UIScene.ConnectionOptions創(chuàng)建一個 UIScene**
** UISceneSession包含了一些配置信息**
public init(session: UISceneSession, connectionOptions: UIScene.ConnectionOptions)
- 管理 Scene 的生命周期
** 實現(xiàn)UISceneDelegate方法的代理對象**
open var delegate: UISceneDelegate?
- 獲取Scene屬性
** Scene的當前執(zhí)行狀態(tài)。
** @available(iOS 13.0, *)
public enum ActivationState : Int {
case unattached 未連接到應(yīng)用程序的狀態(tài)。
case foregroundActive 在前臺運行
case foregroundInactive 在前臺運行正在接收事件
case background 后臺運行
}
open var activationState: UIScene.ActivationState { get }
** Scene 的標題,系統(tǒng)會在應(yīng)用切換器中顯示這個字符串
open var title: String!
- 指定場景的激活條件
** 使用這個屬性告訴UIKIt什么時候你想激活這個場景。
open var activationConditions: UISceneActivationConditions
- 獲取和 Scene 相關(guān)的 session
** 只讀屬性,UIKit為每個場景維護一個會話對象。session對象包含場景的唯一標識符和關(guān)于其配置的其他信息。
open var session: UISceneSession { get }
- 打開URL
** 異步地加載指定的 URL,使用此方法打開指定的資源。如果指定的URL模式由另一個應(yīng)用程序處理,iOS將啟動該應(yīng)用程序并將URL傳遞給它。啟動應(yīng)用程序?qū)⒘硪粋€應(yīng)用程序帶到前臺。
open func open(_ url: URL, options: UIScene.OpenExternalURLOptions?, completionHandler completion: ((Bool) -> Void)? = nil)
- 相關(guān)的通知
@available(iOS 13.0, *) UIKit向應(yīng)用添加了一個場景
public class let willConnectNotification: NSNotification.Name
@available(iOS 13.0, *) UIKit 從應(yīng)用中移除了一個場景
public class let didDisconnectNotification: NSNotification.Name
@available(iOS 13.0, *) 指示場景現(xiàn)在在屏幕上,并相應(yīng)用戶事件
public class let didActivateNotification: NSNotification.Name
@available(iOS 13.0, *) 指示場景將退出活動狀態(tài)并停止相應(yīng)用戶事件
public class let willDeactivateNotification: NSNotification.Name
@available(iOS 13.0, *) 場景即將開始在前臺運行
public class let willEnterForegroundNotification: NSNotification.Name
@available(iOS 13.0, *) 場景在后臺運行
public class let didEnterBackgroundNotification: NSNotification.Name
UISceneConnectionOptions
連接選項
描述:
UIKit 創(chuàng)建scene 有很多原因,它可以響應(yīng)切換請求或打開 URL 的請求,當有創(chuàng)建場景的特定原因時,UIKit 用關(guān)聯(lián)的數(shù)據(jù)填充 UISceneConnectionOptions 對象,并在連接時將其傳遞給代理,使用此對象中的信息進行相應(yīng)的響應(yīng)。例如,如果是打開 UIKit 提供的 url,我們可以在場景中顯示他們的內(nèi)容。
不要直接創(chuàng)建UISceneConnectionOptions對象,UIKit 會自動創(chuàng)建,并將其傳遞給場景代理的scene:willConnectToSession:options:方法。
** 要打開的url,以及指定如何打開它們的元數(shù)據(jù)。
open var urlContexts: Set<UIOpenURLContext> { get }
** 發(fā)起請求的應(yīng)用程序的boundleID。
open var sourceApplication: String? { get }
** 掛起的切換活動的類型。
open var handoffUserActivityType: String? { get }
** 恢復(fù)場景的先前狀態(tài),用于將應(yīng)用還原到以前狀態(tài)的用戶活動信息。
open var userActivities: Set<NSUserActivity> { get }
** 用戶對應(yīng)用程序通知之一的響應(yīng)。
open var notificationResponse: UNNotificationResponse? { get }
** 處理快速動作,用戶選擇要執(zhí)行的操作
open var shortcutItem: UIApplicationShortcutItem? { get }
** 有關(guān)應(yīng)用程序現(xiàn)在可用的CloudKit數(shù)據(jù)的信息。
open var cloudKitShareMetadata: CKShareMetadata? { get }
