iOS13新特性 - SceneDelegate

SceneDelegate是为了支持 iOS13之后的 iPadOS 多窗口口而退出的。Xcode11 默认会创建通过 UIScene 管理多个 UIWindow 的应用,工程中除了 AppDelegate 外会多一个 SceneDelegate。并且 AppDelegate.h 不再有 window 属性,window 属性被定义在了 SceneDelegate.h 中,SceneDelegate 负责原 AppDelegate 的 UI 生命周期部分的职责。

  • iOS13之前
    AppDelegate的职责全权处理 App 生命周期和 UI 的生命周期。


    image.png

这种模式完全没有问题,因为只有一个进程,只有一个与这个进程对应的用户界面。

  • iOS13 之后
    AppDelegate 的职责是处理 App 的生命周期;
    新增的 SceneDelegate 是处理 UI 的生命周期。
image.png
image.png
  • 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,并且又不需要多个窗口的功能,可以直接删除以下内容:
    1.info.plist文件中的Application Scene Manifest 的配置数据
    2.AppDelegate 中关于 Scene 的代理方法
    3.SceneDelegate 的类
    4.实现UIApplicationDelegate的方法

如果使用纯代码来实现显示界面,需要在 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 中也没有这个属性了,转交给 SceneDelegate 的 willConnectToSession: 方法进行根控制器设置:
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()
    }

下面是关于UIScene 以及 SceneDelegate 的一些介绍了。

UIScene 的介绍

表示应用程序用户界面实例的对象。

描述:

  • 1.UIKit 为用户或应用程序请求的每个应用的 UI 实例创建一个场景对象(也就是一个 UIScene),也就是UIWindowScene(继承于 UIScene) 对象。

  • 2.UIScene 会有一个代理对象(实现 UISceneDelegate),用以接收 UIScene 的状态改变,例如,使用它来确定你的场景何时移动到背景。

  • 3.通过调用UIApplicationrequestSceneSessionActivation:userActivity:options:errorHandler:方法去创建一个 scene。UIkit 还根据用户交互创建场景。

  • 请注意,我们使用的都是 UIWindowScene ,而不是 UIScene。

方法列表
  • 创建一个实例
** 通过制定的UISceneSession对象和UIScene.ConnectionOptions创建一个 UIScene**
** UISceneSession包含了一些配置信息**
public init(session: UISceneSession, connectionOptions: UIScene.ConnectionOptions)
  • 管理 Scene 的生命周期
** 实现UISceneDelegate方法的代理对象**
open var delegate: UISceneDelegate?
  • 获取Scene属性
** Scene的当前执行状态。
** @available(iOS 13.0, *)
    public enum ActivationState : Int {
        case unattached  未连接到应用程序的状态。
        case foregroundActive 在前台运行
        case foregroundInactive 在前台运行正在接收事件
        case background 后台运行
    }
open var activationState: UIScene.ActivationState { get }

** Scene 的标题,系统会在应用切换器中显示这个字符串
open var title: String!
  • 指定场景的激活条件
** 使用这个属性告诉UIKIt什么时候你想激活这个场景。
open var activationConditions: UISceneActivationConditions
  • 获取和 Scene 相关的 session
** 只读属性,UIKit为每个场景维护一个会话对象。session对象包含场景的唯一标识符和关于其配置的其他信息。
open var session: UISceneSession { get }
  • 打开URL
** 异步地加载指定的 URL,使用此方法打开指定的资源。如果指定的URL模式由另一个应用程序处理,iOS将启动该应用程序并将URL传递给它。启动应用程序将另一个应用程序带到前台。
open func open(_ url: URL, options: UIScene.OpenExternalURLOptions?, completionHandler completion: ((Bool) -> Void)? = nil)
  • 相关的通知
    @available(iOS 13.0, *)   UIKit向应用添加了一个场景
    public class let willConnectNotification: NSNotification.Name

    @available(iOS 13.0, *)  UIKit 从应用中移除了一个场景
    public class let didDisconnectNotification: NSNotification.Name

    @available(iOS 13.0, *)  指示场景现在在屏幕上,并相应用户事件
    public class let didActivateNotification: NSNotification.Name

    @available(iOS 13.0, *)  指示场景将退出活动状态并停止相应用户事件
    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 创建scene 有很多原因,它可以响应切换请求或打开 URL 的请求,当有创建场景的特定原因时,UIKit 用关联的数据填充 UISceneConnectionOptions 对象,并在连接时将其传递给代理,使用此对象中的信息进行相应的响应。例如,如果是打开 UIKit 提供的 url,我们可以在场景中显示他们的内容。
不要直接创建UISceneConnectionOptions对象,UIKit 会自动创建,并将其传递给场景代理的scene:willConnectToSession:options:方法。

** 要打开的url,以及指定如何打开它们的元数据。
open var urlContexts: Set<UIOpenURLContext> { get }

** 发起请求的应用程序的boundleID。
open var sourceApplication: String? { get }

** 挂起的切换活动的类型。
open var handoffUserActivityType: String? { get }

** 恢复场景的先前状态,用于将应用还原到以前状态的用户活动信息。
open var userActivities: Set<NSUserActivity> { get }

** 用户对应用程序通知之一的响应。        
open var notificationResponse: UNNotificationResponse? { get }

** 处理快速动作,用户选择要执行的操作
open var shortcutItem: UIApplicationShortcutItem? { get }

** 有关应用程序现在可用的CloudKit数据的信息。
open var cloudKitShareMetadata: CKShareMetadata? { get }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容