iOS13 中 View Controller Presentation 的变化

3,724 阅读16分钟

本文翻译自:View Controller Presentation Changes in iOS 13

版权归原作者所有,转载请注明原地址。

术语对照表

原文 译文
View Controller 视图控制器
presentation 显示
modal 模态

介绍

在撰写本文时,WWDC 2019即将结束。和众多 iOS 开发者一样,我正在慢慢消化苹果公司向我们披露的新信息,并将在未来几周(以及几个月)尝试观看尽可能多的视频!

我对自己的应用程序的三个直接目标:

  1. 我当前的应用程序在 iOS 13 上运行没有问题吗? 苹果基于应用程序构建所使用的 Xcode 版本具有很长时间的向后兼容性。历史表明,由 Xcode 10 构建的应用程序在 iOS 13 上的运行表现和在 iOS 12 上一样。但情况并非总是如此。

  2. 使用Xcode 11 / iOS 13 构建我的应用程序是否有效? 使用最新工具构建会给应用程序添加新行为,从而绕过对旧 iOS 版本的向后兼容性。程序会有问题吗?

  3. 我能或者应该做什么,来让我的应用程序更好地工作或利用新的 iOS 特性? 这是最重要的任务,需要花费大量时间进行调查和实现。这就是后话了。

我尚未在真机上安装 iOS 13,但我对目标 #1 进行一些测试:可以通过在 iOS 13 模拟器中安装 Xcode 10 构建的应用程序。 我在博文《iOS 12 中 iPad 导航栏和工具栏高度变化》Wait, What Did You Say? 部分解释了这个过程。(该文是一年前写的,所以讨论的是 iOS 12 上用 Xcode 9 构建,但过程是一样的。)

我还在研究目标 #2,但根据我的初步测试和阅读其他开发人员做出类似发现的推文,我发现在使用 Xcode 11 构建时,我的应用程序发生了许多行为变化。我还有很多要看的视频以及要消化的信息,但在这篇文章中,我将着重讨论在 iOS 13 中视图控制器显示的十分显著的、并可能引发破坏的改变。

默认模态显示样式改变

默认模态显示现在是页表(page sheet),而非全屏(full screan)。 modalPresentationStyle的头文档写道:

从 iOS 13.0开始,iOS 默认设为 UIModalPresentationAutomatic,在以前的版本上默认为UIModalPresentationFullScreen

默认情况下,UIViewControllerUIModalPresentationAutomatic 解析为 UIModalPresentationPageSheet,但其他系统提供的视图控制器可以将UIModalPresentationAutomatic 解析为其他具体的显示样式。

这一变化的结果在 iPhone 和 iPad 上有所不同。

iPhone上的模态演示文稿

iPhone 上的表单(form sheet),页表(page sheet)或弹出窗口(popover)显示将调整为全屏,除非使用 UIAdaptivePresentationControllerDelegate 方法来阻止调整。例如,“设置”屏幕可能会显示为表单,在 iPhone 上会以全屏显示,在 iPad 上则会以较小的表单尺寸显示。从技术上讲,外观/适应性取决于水平尺寸等级。 横屏(landscape) iPhone Plus 和 XS Max 设备上的表单/页面/弹出式演示文稿不会填满屏幕,因为它们是常规宽度。 iPad 外观取决于侧边栏(slide over)和分屏(split-screen)多任务的大小。如果由于用户调整或移除分屏而导致水平尺寸等级发生变化,显示的视图控制器也会随之改变。

以下截图显示了三种情况下 iPhone XS 上的表单 presentation:Xcode 10 构建在 iOS 12 上运行,Xcode 10 构建在 iOS 13 上运行,Xcode 11 构建在 iOS 13 上运行。

用 Xcode 10 构建的 iOS 13 的 iOS 12 向后兼容性导致全屏显示。在 iOS 13 中,分组列表(grouped table)视图的样式已更改为在没有标题时隐藏第一个 section 上方的空间。Xcode 10 / iOS 12 构建的程序在 iOS 13 上运行时行为竟然不同,这点我没有想到。

当然,iOS 13 中最大的变化就是卡片式的外观😍。显示视图控制器的尺寸被缩小了,并且在其后面仍然略微可见顶端。 根视图控制器后面的窗口在显示视图控制器后面和状态栏下面也略微可见(它自动改变了颜色, 真棒!)默认的黑色窗口背景看起来很棒,特别是在刘海屏上。 我的一些应用程序将窗口背景设置为白色(原因不明)并且看起来非常难看。我很快就解决了!(?)

View Controller 整体下移

如果被显示的视图控制器在另一个视图控制器显示出来(以及如此下去),则卡片会用一个漂亮的动画堆叠。请注意,只能看到最顶层视图控制器及其显示视图控制器。

Modally-presented Settings screen presenting a modal INUIAddVoiceShortcutViewController on iOS 13

另一个潜在重要差异是显示视图控制器会发生什么。完全覆盖视图控制器的全屏显示将导致显示视图控制器 从视图层次结构中移除(an over full screen presentation 可防止发生这种情况(?))。当然,显示视图控制器 必须要保留在视图层次结构中,因为它仍然可见。但是,即使用户一次只能看到两个视图控制器,重复不断的显示视图控制器也都不会从视图层次结构中删除其下方的 VC。

(view Disappear 方法不好使了?)

尺寸变化

新的卡式外观意味着显示视图控制器 在 iOS 13 上并不像在 iOS 12 上那么高:

我就是要全屏!

明确要求全屏模态显示将覆盖此新行为。但是,这可能会破坏 iPad 上的表现(如果你需要表单或页表)。请抵制检查设备习语的诱惑(?),并在 iPhone 和 iPad 上使用不同的 显示风格。过去几年的经验告诉我们,不应该根据设备类型或屏幕尺寸做出假设。如果你想要 iPad 上的表单/页表外观但在 iPhone 上全屏,你应该使用UIAdaptivePresentationControllerDelegate在紧凑宽度环境中适应全屏(包括较小的 iPad 多任务尺寸)。

iPad 上的模态显示

表单

iPad 上显示的表单在 iOS 13 中保持不变:

页表

前面提到了,iOS 13 上的默认模态显示样式现在是页表。iPad 上页表的尺寸在竖屏(portrait)和横屏(landscape)上都变化了。

和 iOS 12 一样,As on iOS 12, the readable content guide changes size as the content size category changes. The actual size seems to be different on iOS 12 and 13 at some content size categories.(?)

页表本身在 iOS 13 中也变大了,as the content size category increases. Here’s the Extra Extra Extra Large content size category (the largest size available without enabling the larger accessibility sizes):

其他显示

请记住modalPresentationStyle的头文件文档:

默认情况下,UIViewControllerUIModalPresentationAutomatic解析为UIModalPresentationPageSheet,但其他系统提供的视图控制器可以将UIModalPresentationAutomatic解析为其他具体的显示样式。

我不是100%确定“其他系统提供的视图控制器”的所有规则,但我确实发现在没有设置模态显示样式的情况下,显示分屏视图控制器会给出全宽卡片外观:

大多数应用程序都不会显示分屏视图控制器,但它是我的 Adaptivity 应用程序中的一个视图。我已经更新了我的代码以明确地显示分屏视图控制器全屏,因为大多数使用分屏视图控制器的应用程序会将其用作根视图控制器。

滑动以退出(dismiss)

影响 iPhone 和 iPad 的另一个重要变化是,非全屏模式显示(弹出窗口除外)可以通过向下滑动以交互方式退出。显示视图控制器可以动画回到全屏:

请注意,在此示例中,我已将“关于”屏幕推入(push)我的模态显示的设置屏幕的导航控制器上。即使导航控制器没有显示其根视图控制器,仍然可以进行交互式退出。

别滑我!

对于推入真正的“模态”视图控制器的应用程序(直接或在模态显示的导航控制器的堆栈上),此更改尤其重要。如果依靠用户点击“完成”按钮(或类似)或导航回导航控制器堆栈的顶部以解除模态呈现的视图控制器,则新的滑动以退出的行为可能会破坏你的应用,因为你的退出按钮的 handler 程序将不会执行。

例如,在我的番茄计时器应用程序Pommie中,用户可以导航到“设置”屏幕中的子屏幕,并添加或编辑计时器配置文件(特定类型任务的工作/休息时间的配置):

Pommie 这个案例中,我认为如果用户通过从顶层屏幕或定时器配置文件子屏幕(以及大多数其他子屏幕)滑动而退出整个设置视图控制器是可以的(并且安全)。用户可能会期望他们可以滑动退出,我希望我的应用程序表现得像 iOS 13的良好公民。但是,我觉得我应该在添加/编辑计时器配置文件屏幕中防止这种情况发生,因为存在丢失更改的风险。用户滑动退出时是期待更改被放弃或保存呢??我不清楚他们的意图。

解决此问题的一种方案是新的视图控制器属性:isModalInPresentation。头文件文档中是这样写的:

当你希望强制视图控制器显示进入模态行为时,在视图控制器上设置modalInPresentation。当它设为 YES 时,显示将阻止交互式关闭并忽略被显示的视图控制器边界之外的事件,直到将其设置为 NO。

为了让我的设置屏幕在 iOS 13上表现和 iOS 12一样,我可以给模态显示导航控制器设置isModalInPresentation为 YES。如果用户尝试向下滑动以退出,则视图控制器会稍微移动但会阻止用户的操作,并且无法退出。

该属性可以随时更改,比如允许用户有尚未显式保存的可能丢失的更改时直接退出。但是一旦进行了更改(可能在你点击“保存”按钮后),您可以设置isModalInPresentation为通过滑动防止退出。然后,这将强制用户显式,并点击取消或保存按钮。或者,您可以检测失败的尝试以解除并显示一个操作表,询问他们是否放弃或保存更改(有关详细信息,请参阅下一节)。

Apple一直很聪明:设置isModalInPresentation我的添加/编辑定时器配置文件视图控制器将阻止在显示该视图控制器时通过滑动解除设置屏幕。只要我取消或保存并弹出堆栈中的某个级别,就会再次启用交互式滑动。事实上,他们非常聪明:如果导航堆栈上的任何视图控制器isModalInPresentation设置为,则禁用交互式解雇true。这意味着如果我的添加计时器配置文件屏幕或微笑模式屏幕要在堆栈上推送任何更多的子屏幕,他们将不需要设置isModalInPresentation以防止解雇。视图控制器越往上,所述堆的存在不设置isModalInPresentation将防止解雇。 除了,当它没有!在我的测试中,如果视图控制器有一个搜索栏,isModalInPresentation搜索控制器上的属性优先于搜索栏处于活动状态。也就是说,它能够滑动即使包含搜索栏视图控制器设定了搜索期间以关闭isModalInPresentation到true。这可能是第一个测试版中的错误,但可能与搜索栏如何呈现搜索控制器有关。根据您应用的要求,您可以: isModalInPresentation直接在模态呈现的导航控制器上设置(而不是在堆栈中显示的所有单个视图控制器上) isModalInPresentation在搜索控制器本身上设置,以便当用户点击搜索栏时显示它,它将控制是否可以进行交互式解雇。

退出检测

如前所述,当使用“取消”,“完成”或“保存”按钮退出模态显示的视图控制器时,某些应用程序可能需要执行某些代码。例如,您可能需要在游戏中重新启动计时器,或者根据用户在显示的视图控制器中更改的某些信息进行操作。如果用户通过滑动退出,则不会执行该代码。您的按钮未被按下,因此不会调用其动作处理程序。这可能会破坏您应用的行为。 避免此问题的最简单方法是使用防止交互式退出的isModalInPresentation。用户将必须点击一个按钮来关闭视图控制器,就像他们在iOS 13之前所做的那样。还有另外一种方法……

iOS 13增加了一些新的UIAdaptivePresentationControllerDelegate方法。这些f方法允许另一个对象(通常是显示视图控制器)控制是否应该允许交互式退出(使用isModalInPresentation的替代方法),并且在交互式退出开始或完成时被通知。这些方法已有详细文档,并在WWDC 2019 224: Modernizing Your UI for IOS 13中有清楚的解释(从15分钟开始)。注意presentationControllerWillDismiss方法可能会多次调用,如果用户开始滑动以退出,改变主意然后再次滑动。presentationControllerDidDismiss方法是是当你当前按下取消、完成或保存按钮时需要执行额外代码的地方(当然,您不需要关闭被显示的视图控制器)。如果以程序的方式关闭视图控制器,则不会调用这些方法。因此,即使在 iOS 13上运行,你仍然需要在触发退出的按钮处理程序(或你自己的委托)中执行代码。

presentationControllerDidAttemptToDismiss委托方法很有趣。如果用户尝试滑动以退出,但是isModalInPresentation阻止了退出,则会调用它。WWDC 视频建议显示一个操作表(在 iPad 上将是一个弹出框)询问用户是想放弃还是保存更改。如果呈现的视图控制器具有“取消”和“保存/完成”按钮,则这似乎是一个非常好的主意:如创建新笔记,编辑对象的属性等。

对于带有“取消”和“保存”按钮的导航堆栈上的嵌套视图控制器(例如,Pommie的“设置”屏幕中的“计时器配置文件”屏幕),我认为它更复杂。执行保存的代码可能在视堆栈中的高一个级别的视图控制器中(顶视图控制器的委托),而不是在作为UIAdaptivePresentationControllerDelegate的对象中。尝试将用户的选择路由到可以执行保存的对象可能非常混乱。在我自己的应用程序中,我想我会阻止需要显式的取消/保存操作视图控制器中的退出,如果它们不在导航堆栈的顶部。

资源

WWDC 2019视频将是发现iOS 13中已发生变化的最佳位置,您需要对应用程序进行哪些更改才能使其在Xcode 11上构建时正常工作,以及您可以采取哪些措施来改进它们以利用它们新功能。这里有一些开头:

结论

到目前为止,我还没有发现在iOS 13上运行的Xcode 10构建的应用程序存在任何问题.Apple的向后兼容性在这方面确实有帮助。看到分组表视图的外观发生了变化,我感到有些惊讶。 Xcode 11构建需要一些小的修复来处理本文中讨论的模态演示中的变化。可能会有更多我尚未发现的变化,而且在我开始进行更改以利用新的iOS 13功能之前。 仔细测试您的模态演示文稿(尤其是使用搜索栏)并向下滑动以查看您自己的应用程序中发生的情况!决定是否允许用户滑动以消除模态呈现的视图控制器(可能是在导航堆栈中逐屏显示),并用于isModalInPresentation获取所需的行为以防止错误滑动导致意外数据丢失。为了更灵活和控制,请使用UIAdaptivePresentationControllerDelegate。

Adaptivity iOS App

The screenshots in this article were taken from the iOS simulator running my Adaptivity iOS app. Adaptivity is a tool for developers and designers to visualise the different screen sizes, layout margins, readable content guides, bar heights and Dynamic Type sizes that a modern, adaptive, iOS app uses when running on different devices and iPad multitasking sizes. The iOS 13 version of Adaptivity includes views for dark mode, browsing System Colors, browsing System Images (SF Symbols), and multiple scenes on iPad. More screenshots and information on all the features is available on my web site.

译者注:这个 App 可能已下架或者在国内 App Store 无法获取,是付费 App。

你可能会喜欢的其他文章

如果你是 iOS 开发人员,你可能会对我的长篇系列文章感兴趣,这些文章展示了应用程序如何适应更新的设备大小,具体取决于构建它们所用的 Xcode 版本:

这些文章中的大多数屏幕截图都是从运行我的 Adaptivity iOS 应用程序的 iOS 模拟器中获取的。有一个 iPhone-only 的应用程序版本,以说明 iPad 上的 iPhone-only 应用程序如何显示(在iOS 12中已更改)。

你可能没有意识到 iOS 12中有 iPad 导航栏和工具栏高度的变化。或许您想要修复 Xcode 的 iPhone XS、XS Max 和 XR 模拟器名称以及 iOS 版本

我还写过关于iOS上的外部显示支持使用多个版本的Xcode的文章。后者在 WWDC 和9月份 iOS 公开发布期间特别有用。

如果你发现这些文章中的任何一篇有用,那么请查看我在iOS App Store中的应用程序,看看是否有任何您想要下载的内容(特别是付费内容😀)。如果你使用很多Xcode项目,你可能会喜欢我的 Mac 菜单栏实用程序XcLauncher