iOS 13 Xcode11 中的 Scene Delegate_Code&Ocean的博客-程序员资料

技术标签: # Xcode  iOS 13  Xcode11  SceneAppdelegate  

如果将Xcode更新到11, 创建项目。默认会创建SceneDelegate.swift, 那么问题来了, 这个代理用来干嘛的了?

在这篇文章中,我们将探索iOS13和Xcode11的改变。我们着重介绍scene和Delegates , 看看他们事如何影响SwiftUI,Storyboard和Xib构建的UI.

我们将学习到:

  • AppDelegate和SceneDelegate
  • 程序启动时,他们如何一起工作的
  • 怎么设置app的scene deleagate
  • 在Storyboard和SwiftUI不同环境中如何使用scene delegate

系好安全带,发车。。。

这篇文档项目环境Xcode11 和 iOS13

AppDelegate

我们对于AppDelegate非常熟悉,他是一个App启动的入口,其中的 application(_:didFinishLaunchingWithOptins: ) 方法是系统操作唤醒的第一个方法。

AppDelegate 遵守UIKit框架的UIApplicationDeleaget ,但是在iOS13中app delegate发生了改变 ,我们能很快发现。

以下是iOS12 app deleget 的常规操作:

  • 设置第一个ViewController,我们叫做它root View Controller吧
  • 配置app设置和启动模块,比如登录,连接服务器等
  • 注册推送通知回调,响应发送到app的推送通知
  • 响应app生命周期事件,比如进入后台,唤醒和退出应用

使用Storyboard启动的app, app delegate都是千篇一律的。因为它只返回true 就像下面这个方法:

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

一个简单的使用Xib的app, 需要设置自己的根控制器,就像这样:

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
}

在上面的代码中,我们创建了一个控制器, 并且把他放在导航栏控制器中,把他分配为给 UIWindow对象的rootController属性。 这个window对象是 app delegate的属性, 是我们app拥有的一个window.

app的window是iOS中一个非常重要的概念, 一般一个window就是一个app,而且大部分iOS 应用只有一个window, 它就是你应用 UI界面的可视, 用户点击事件, 提供后台显示内容。 当然这里的window和微软的Windows不是一个东西, 是不同的概念(谢谢你 Xerox!)。

好现在我们把重点放在scene delegate。

SceneDelegate

在iOS 13及以上 scene delegate 将扮演 一些 app delegate 的作用, 大多数情况窗口(window)的概念被场景(scene)替换。 一个应用可以有多个场景, 而场景又是作为应用的界面和内容的呈放。

一个app有多个场景的概念来说是特别地, 而且这样就允许您创建多窗口的iOS或者iPadOS应用。在一个 文本处理app中,每一个文档都可以有自己的场景。例如, 用户可以创建一个场景的复制场景, 一次可以运行一个app的多实例。

在Xcode 11中跳转使用scene delegate有三个地方:

  1. 新建一个项目,会自动创建SceneDelegate 类, 它包括 生命周期事件,比如 动作, 注册, 连接等
  2. AppDelegate中有两个关联scene sessions的方法,叫做 application(_:configurationForConnecting: options:) 和 application(_: didDiscardSceneSessions:)
  3. 在 Info.plist文件中的属性列表中有一个Application Scene Manifests(应用场景清单)属性(如下图),可以进行相应配置, 比如类、代理、 storyboard名字等

好,让我们一步一步操作。

1. 场景代理类(Scene Delegate Class)

SceneDelegate类如:

import UIKit

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.
    }


}

场景代理中最重要的方法是 scene(_:willConnectTo:options:), 一般来说它等同于iOS 13之前的 application(_: didFinishLaunchingWithOptions:)方法, 场景被添加到app,这个方法将被调用, 这是配置场景完美的地方。 在上面的代码中, 我们可以设置controller栈,这个设置我们等会儿再说

重点注意SceneDelegate在使用delegate时,当然 一个delegate 也可以响应任何场景,如果你想使用一个代理配置所有场景的话。

SceneDelegate也包含下面这些方法:

  • sceneDidDisConnect(_:) 场景没有连接时调用(等一会儿他会再连接)
  • sceneDidBecomeActive(_:) 当用户开始进入场景时调用,比如从app switcher(应用切换)选中app
  • scenewillResignActive(_:) 当用户停止进入场景,比如进入另一个场景
  • sceneWillEnterForeground(_:) 当用户将要进入前台,比如从后台开始或者唤醒
  • sceneDidEnterBackground(_:) 当场景将要进入后台,比如 app 最小化,但是依然在后台运行  

 

2. 应用代理:场景会话(AppDelegate : Scene Sessions)

在iOS13中, Appdelegate类现在包含了与secene sessions 相关的方法。 创建一个场景的同是 场景对象也会随之创建,场景会话对象跟踪管理场景的相关信息, 方法如下:

  • application(_: configurationForConnecting: optings:) 这个是返回场景配置对象的。
  • application(_:didDiscardSceneSessions:) 你的app用户关闭一个或者多个场景时调用。

现在, 场景会话用于指定一个场景, 比如 "External Display" 或者 "CardPlay", 也被用来存储一个场景的状态, 用于状态恢复非常好用。 状态恢复允许你恢复或者再次创建UI在app启动期间。你也可以分配用户信息到场景会话中, 这是一个你可以放入任何能放入的字典。

application(_:didDiscardSceneSession:) 是非常简单的, 当用户通过应用切换来关闭一个或者多个场景时会调用。你可以在这里释放一些你场景使用的资源,因为他们不在需要使用了。

对比与 sceneDidDisconnect(_:) , 这个方法标识当场景仅仅失去连接,但是不一定丢弃。 它可能会尝试重连, 直到application(_:didDiscardSceneSession:) 使用应用切换标记场景退出。

3.Info.plist中的应用场景清单(Application Scene Manifest)  

你使用的每一个场景都需要在应用场景清单中声明过, 简而言之就是清单列出你应用支持的场景。 大多是app只有一个场景,但是你能创建更多个,比如响应通知或者事件的单独场景。

应用场景清单也是Info.plist文件中的一部分,Info.plist也是一个明确知道应用配置的好地方。该文件属性列表包含应用名字,版本号,支持设备方向等

注意在这里声明session的类型, 而不是session本身。 你的app能支持一个场景, 创建一个场景的副本和使用它来创建多窗口app.

以下是Info.plist的列表大概:

在顶层,你可以看见应用场景清单子项,下面时Enable Multiple Windows ,如果你需要支持多窗口,需要设置为YES. 再往下是一个声明应用内场景的Applicaiton Session Role数组。 另一个部分能用于声明外部场景。

最重要的信息是Application Session Role 数组部分, 包括:

  • Configeration Name 配置的名字,必备的
  • 场景的类名, UIWindowScene
  • Delegate Class Name 场景代理类名,一般是SceneDelegate
  • 场景初始化storyboard的名字

storyboard的名字部分主要提醒用户主Storyboard,以便于在Xcode 12 的项目属性配置中看到。 一般基本iOS 应用中, 如果你不使用场景这就是你设置或改变主要Storyboard的地方【译者发现,如果需要改变主显示Storyboard,只能在这里修改,直接在项目的General -> Deployment Info -> Main Interface中修改是没有效果的。 】。

如何用SceneDelegate、 AppDelegate中的scene session 应用场景清单怎么创建多窗口app?

  • 首先,我们已经了解过Scenedelegate类, 它管理场景的声明周期,响应事件(比如sceneDidBecomeActive(_:)和 sceneDidEnterBackground(_:))
  • 之后,我们检查Appdelegate的新方法,它管理场景会话,提供场景配置数据, 用户丢弃场景响应
  • 最后,我们查看应用场景清单(Application Scene Manifest),它罗列出你应用支持的场景, 代理类和初始化storyboard

太棒了!有了初步了解,我们看看用Xcode11创建UI,看看是如何影响的。

基于SwiftUI的Scene Delegate

iOS13最简单创建项目方法是使用SwiftUI, 简单来说  SwfitUI创建的项目大多是都是通过SceneDelegate设置初始化UI的

首先看看SwiftUI的应用场景清单长什么样子:

这是非常标准的,对于默认app,哪里标准了? 使用Default Configuration标识不包含的Storyboard Name Set 。记住, 如果你想支持多窗口,需要吧Enable Multiple Windows 设置为 YES.

我们跳过AppDelegate, 因为它非常标准,只返回true.

下一步, 看看 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()
        }
    }

上面的代码做了什么了?

首先,认真考虑scene(_:willConnetectTo:options:)协议方法(当一个新场景加入),提供scene对象和session, 这个UIWindowScene对象是app创建的,如此一来你就不需要手动创建。

之后,这个window属性被使用,应用仍然有UIWindow的window对象,只不过现在他们是场景的一部分。在代码中,在if let 闭包内,你可以清楚的看见使用scene参数初始化的UIWindow实例。

设置window的rootViewController属性,然后这个window变为可见(make key add visible),目的是把它放在UI层的最前面。

使用SwiftUI,你要注意,被创建的ContentView作为root view controller 通过使用UIHostingController, 这个控制器把SwiftUI基本视图放在屏幕上。

方法中有一点注意,类型为UIScene的scene参数,实际上UIWindowScene类型的实例。使用as 可选解析(到目前为止,创建的场景通常是UIWindowScene类型,但我猜想在未来,我们会看到更多类型的场景。)

所有的看起来很复杂,但是总体来看非常简单。

  • scene delegate 中配置场景,合适的时间,就是当scene(_:willConnectTo:options:) 被调用时。
  • app delegate 和Manifest ,有默认的配置,不需要引入storyboard
  • scene(_:willConnectTo:options:)方法中创建SwiftUI的视图, 放在hosting controller中,分配它为window属性的root view controller,把他们设置为UI层最前端就好了。

非常棒!让我们开始

你可以使用Xcode11创建基本项目, 选择SwfitUI, 通过选择File -> New -> Project, 之后选择 Single View App, 最后选择 SwiftUI作为User Interface

 

基于Storyboard的SceneDelegate

Storyboards, XIBs, 是创建UI的有效的方式。 但是在iOS 13上是一样的。在以后,我们会看到越来越多SwiftUI 应用, 但是现在, storyboard更常用。

有趣的是,你无法多余操作,只需要选择File-> New -> Project, 选择Single View App, 最后选择 Storyboard作为User Interface , 就完成了。

下面使步骤:

  • 就像之前所述,在Info.plist,你发现Main storyboard 在 Application Scene Manifest的字典中
  • 默认app delegate将使用默认的场景配置
  • 默认scene delegate  设置 UIWindow对像,使用Main.storyboard创建初始化UI

 

设置你应用的编程方式

很开发者使用代码创建UI, 随着SwiftUI的崛起, 我们能看到更多。 那如果你不是用Storyboard,而是使用xib来创建你得app UI , 来看看场景代理如何适配它吧。

首先, app delegate 和 Applicaton Scene Manifest 使完整的, 默认设置的。 我们没有使用storyboard,我们要在 SceneDelegate 的 scene(_:willConnectTo: options:)方法内设置初始化控制器。

就像这样:

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()
        }
    }

 我们来看看发生了啥:

  • 就像之前,我们持有UIWindow类型的window属性,它是用windowScene对象初始化的,该对象是由scene参数进行类型转换的
  • 在 if le闭包内, 就像上面的代码, 这就是在iOS 12及之前设置根控制器的方式在AppDelegate, 你厨师化一个view controller, 把他放在导航栏控制器里, 设置给rootViewController
  • 最后,window常量分配给window属性, 然后让它可见,把他放在屏幕最前面。 

很简单,对吧? 最核心的是,把你之前在app delegate中代码移到scene delegate ,配置Applicaton Scene Manifest

还在找怎么为已存在的项目添加场景支持吗? 看这个吧

 

深入学习

完美了!这只是示例的一小部分, 随着我们的深入,场景代理允许你添加多窗口应用

你可能已经学会怎样为SwiftUI、Storyboard设置场景代理, 我们也看了使场景工作的三部分:app delegate , scene delegate , Application Scene Manifest , 非常棒!

想要学习更多吗? 通过下面资源学习吧:

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_14920635/article/details/103013745

智能推荐

使用logrotate进行日志分割,使用crond定时进行日志分割出现的问题_Sand_Ng的博客-程序员资料

logrotate使用中出现过的问题:1、logrotate 的配置文件必须是 root 644权限2、logrotate配置中的日志的目录权限不能为777,可以为7553、使用create和compress进行日志转存时,会有概率出现转存后的日志没有进行压缩,增加delaycompress4、使用copytruncate可以避免上面的那个问题,但是日志较大时会出现时间转存时间很久5、当即将生成的...

0053-使用OpenCV进行视频稳像(去抖)的代码_清溪算法的博客-程序员资料_视频帧去抖动 opencv c++

这里暂时就不做代码讲解了,代码亲测可用,里边的注释已经比较详细了,不明白的请自行查阅相关资料。代码如下:代码中用到的视频下载链接:https://pan.baidu.com/s/1bpwI1Uv 密码:s65y//opencv版本:OpenCV3.0//VS版本:VS2013//Author:qxsf321.net#include <opencv2/opencv.hpp&...

Mybatis-plus IPage分页常见问题(坑)_V5放纵丶的博客-程序员资料

Mybatis-plus IPage分页常见问题(坑)1.TooManyResultsException观前提示:本文所使用的IDEA版本为ultimate 2019.1,JDK版本为1.8.0_141。1.TooManyResultsException最近在使用Mybatis-plus的IPage插件分页时,出现了以下的莫名其妙的错误Resolved [org.mybatis.spring.MyBatisSystemException: nested exception is org.apach

libcurl的curl_easy_perform奔溃问题_typ2004的博客-程序员资料_curl_easy_perform 崩溃

curl_global_init(CURL_GLOBAL_ALL);curl_global_cleanup();这两个是libcurl全局函数,负责环境的初始化和清理。非线程安全,对于多线程来说,请不要再每个线程里都初始化和清理。一个简单的做法是在主线程里初始化和清理。This function sets up the program environment tha_1671465600

Java项目优化_麦芽糖0219的博客-程序员资料

Tomcat配置优化 调整tomcat参数进行优化 调整JVM参数进行优化 解读jvm字节码 代码优化

JOL工具的使用_weixin_41012215的博客-程序员资料_jol使用

欢迎使用Markdown编辑器你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。新的改变我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:全新的界面设计 ,将会带来全新的写作体...

随便推点

数据质量监控工具-Apache Griffin本地安装和调试_豆浆~油条的博客-程序员资料_apache griffin

数据质量监控工具-Apache Griffin本地安装和调试1 、Apache Griffin简介Griffin起源于eBay中国,并于2016年12月进入Apache孵化器,Apache软件基金会2018年12月12日正式宣布Apache Griffin毕业成为Apache顶级项目。Griffin是属于模型驱动的方案,基于目标数据集合或者源数据集(基准数据),用户可以选择不同的数据质量维度...

【Scikit-Learn 中文文档】集成方法 - 监督学习 - 用户指南 | ApacheCN_秋枫墨客-Z的博客-程序员资料

中文文档: http://sklearn.apachecn.org/cn/stable/modules/ensemble.html英文文档: http://sklearn.apachecn.org/en/stable/modules/ensemble.html官方文档: http://scikit-learn.org/stable/

黑马头条项目详解_流浪の青春的博客-程序员资料_黑马头条项目解析

黑马头条项目是模拟今日头条开发的项目,总共分为十六章,下面给大家手写总结一下项目内容,希望给小伙伴们解答困惑(从今天开始,会不断更新的哦) 一. 环境搭建二. 平台管理端数据准备三. 用户认证四. 自媒体素材管理五. 自媒体文章发布六. kafka及第三方接口七. 自媒体文章审核八. 分布式任务调度及人工审核九. APP端基本功能展示十. APP端用户行为处理十一. APP端评论系统开发十二. APP端文章搜索十三. 新热文章计算十四. 项目部.

Android 5.1 如何内置APK在/system/priv-app目录下的Android.mk_gnnulzy的博客-程序员资料_android 增加内置app

Android4.4 增加的/system/priv-app 跟 原来的/system/app 有啥区别呢?/system/priv-app中包括Launcher,systemui, settingsprovider等,均是系统的核心应用,这些应用能使用系统级的权限,4.4之前的所有/system/app下的软件都能使用系统级的权限。Androi

基于nodej脚手架express-generator,生成express项目_weixin_30670151的博客-程序员资料

初始化一个Express项目,通过生成器工具express-generator快速生成了一个Express应用。1.安装express生成器  检查express版本  $express --version 可以查看版本npm i -g express-generator2.初始化一个express引用express express-app目录结构:...

推荐文章

热门文章

相关标签