本文最初发布于 Medium 网站,原作者 Jordan Morgan 。本文经授权由 InfoQ 中文站翻译并分享。
我们每个人都应该听说了,Apple 发布了 iPhone X。随之而来的,是新推出的自动隐藏在手机屏幕底部的一个横条,官方称其为“Home 指示键”。它唤起了用户对 iPhone 物理 Home 按钮的怀旧感。
对于消费者而言,这意味着在硬件和软件上的一个新奇迹,每次预定时需要投入更多的钱。但是对于很多开发人员而言,这意味着应如何去处理这个鬼东西。感谢上帝,答案非常简单。
本周,我们将介绍 Apple 已为我们给出了的 Home 指示键处理技术。
首先
并非每天都有新视频随硬件发布,但这恰恰是在此之后发生的事情:
在“为 iPhone X 设计”的活动中,苹果的终身设计大师 Mike Stern 制定了一些基本规则。一碗水端平,在使用下面的新功能之前,希望你首先踩住刹车,看一下自身情况是否符合如下规定:
- 尽量避免在 Home 指示键附近做交互控制,尤其是通过手势识别驱动的控制。
- 不能隐藏 Home 指示键,也不能在四周做装饰,或是尝试去更改其外观。这些规则同样适用于位于 iPhone 顶部的相机面板(Bezel)
- 通常除非观看体验(即视频、照片幻灯放映等)很差,否则通常不应隐藏 Home 指示键。
原文太长了,读不下去了!。反正 Apple 的意思就是在大部分情况下,我们都不要去骚扰那个可怜的 Home 指示键。
但是,本文介绍的正是其中的特例。
添加 UIViewController
无论你是否喜欢根据每个控制器处理状态条,或者你纯粹是对 Home 指示键看不惯,Apple 依然继续延续了因人而异的决策方式,而非选择去满足大众的设计。
隐藏 Home Indicator 的机制在本质上类似于状态条的处理:
class ViewController: UIViewController { override func prefersHomeIndicatorAutoHidden() -> Bool { return true } }
我们在前面说过,此类场景是个别情况,因此如上实现缺省返回 False 值。但在文档中特别提及:
系统也考虑到了个人喜好,返回 YES 并非确保会去隐藏 Home 指示键。
文档中似乎并未提及,为什么或是什么时候 UIKit 不会遵循开发人员选择的偏好。文档认为,在 Apple 看来是最好的方式,它就会强制执行这一方式,无论程序返回的布尔值是什么。对此,肯定会有一些 Stack Overflow 帖子讨论这一问题。
此外,这一问题看上去十分明显,但可能是开始产生混淆的一个源头。值得特别注意的是,函数名结尾是 autoHidden 但是并未隐藏,其实只能说明函数返回 True 意味着 UIKit 只有在其准备好之后才会去隐藏 Home 指示键(正常情况下,如果控制器在数秒时间范围内并未接受到任何触摸事件),而不是立刻去隐藏。
UIKit 的信号处理
我们看一下类似的状态条 API。我们并不能仅是重写 API,或是将 API 赋予一个有条件地控制重写函数的变量。我们可使用另一个新的添加到视图控制器的稳健函数族,setNeedsSomethingDone
:
class ViewController: UIViewController { var shouldHideHomeIndicator = false override func prefersHomeIndicatorAutoHidden() -> Bool { return shouldHideHomeIndicator } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) self.shouldHideHomeIndicator = true self.setNeedsUpdateOfHomeIndicatorAutoHidden() } }
它可用于直接分配(Pass Through)函数,因为它只是向 UIKit 发出信号,告知 UIKit 我们已经更改了前面选择用于 Home 指示键可见性的值。不同于状态栏的是,从技术上看它并非立刻产生动画效果(Animatable),因为 UIKit 是根据自身约定执行隐藏动作。所以,下面的代码并不会产生任何效果:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { self.shouldHideHomeIndicator = true UIView.animate(withDuration: 1, animations: { self.setNeedsUpdateOfHomeIndicatorAutoHidden() }) } }
对setNeedsUpdateOfHomeIndicatorAutoHidden()
的简单的赋值和调用,将轻微修改组件的 Alpha 属性产生淡入淡出。无论该组件是否位于一个动画块(Animation Block)中。
Container 控制器
视图控制器(View Controller)的另一个新添加特性,是告知 UIKit 一个子视图控制器是否应该控制 Home 指示键可见性的机制。如果你具有足够丰富的 iOS 经验,你可能会驾驭容器视图控制器,更好地提升抽象和封装模式。
其中包含的这些控制器可能会发现自身已经近乎屏幕底部。如果是这样,你可能想要抛开 Home 指示键做事。一个简单的重写就可解决这个问题。它会返回被掩盖(Obscure)的实例,或是去掩盖一些实例。代码如下:
override func childViewControllerForHomeIndicatorAutoHidden() -> UIViewController? { return myChildController }
如果你已经指定了子控制器去标识可见性,那么重写我们前面所讨论的函数的担子现在落在了子控制器身上:
class MyChildViewController: UIViewController { override func prefersHomeIndicatorAutoHidden() -> Bool { return true } }
函数的签名允许返回值为 Nil。在此情况下,UIKit 将查看当前做出决策的控制器。如果我们已经选择不重写函数,那么决策就是“显示 Home 指示键”。
我们也可以在运行时做出决策。UIKit 将再次请求调用我们刚刚讨论过的直接分配(Pass Through)函数,通知框架应再一次查询prefersHomeIndicatorAutoHidden()
:
override func childViewControllerForHomeIndicatorAutoHidden() -> UIViewController? { return myChildController } func initializeChildController() { myChildController = MyChildController() self.setNeedsUpdateOfHomeIndicatorAutoHidden() }
事情就是这样。
虽然我们可以将其看成一种更深思熟虑的过程,需要应用于日常 iOS 事件(即处理控制器)中,但是我们会发现 API 与现有的处理类似问题的 UIKit 函数几乎相同。
更新:读者问题解答
新的 Home 指示键是否也会位于网站底部状态条之上?
解答详细列出于 webkit.org 中:
设计适用于 iPhone X 的 Web 网站
开箱即用,在新 iPhone X 的显示屏上,Safari 可严丝合缝地显示已有的网站。
我已不再是 Web 开发的酷爱者了,但是这看上去是如下的元标签(meta tage)解决了自动插页问题:
<meta name='viewport' content='initial-scale=1, viewport-fit=auto'>
默认值是 auto,即允许插页内容。我们也可以重置该值,设置整个屏幕的显示方式。如果喜欢使用全屏,有一个新的 CSS 函数constant()
,允许使用预定义常量在考虑安全区域的情况下填充元素四周。这类似于 iOS 的safeAreaLayoutGuide
API.
webkit.org 的帖子给出了一个例子:
.post { padding: 12px; padding-left: constant(safe-area-inset-left); padding-right: constant(safe-area-inset-right); }
Bogdan 的观察更理性:
我不理解为什么 Apple 不是默认关闭 Home 指示键,或是至少给用户一个选择去关闭它。尽管这是一个吸引新用户使用 iPhone 的特性,但是最终(可能只需使用 iPhone 十分钟)每个用户将记住如何切换 App,这时 Home 指示键就成为一个烦人和干扰使用的横条。我说得不对吗?
说得好!
正如iPhone X 的“刘海”(notch)已经不仅仅是看上去那一块异形状槽,它已融入到手机的硬件中,并会成为iPhone 的品牌辨识,我认为在软件上具有相似特性的就是Home Indicator。它是手机的DNA 组成。此外,我打赌Apple 认为它的存在将会引领用户体验。它避免了“等等,为什么现在不见了?”、“它何时显示?”、“它何时隐藏?”、“在显示它时是否可以回到主界面?”之类的问题。
也就是说,我完全同意你的看法,Home 指示键持续出现在屏幕上是有些多余。但是我还没有用上iPhone X,因此在实际使用之前我不做任何评论。
你是否了解,对于游戏等全屏 App,Home 指示键的使用情况如何?它是否会像在正常 iPhone 中通知和控制中心中设置的那样,被两次滑动激活?
我知道有一个 API 可以重写该行为,但是 Apple 真的、真的、真的不希望开发人员这样做。那么它适用于哪些情况呢?当然是全屏游戏。下面给出在“用户接口指南”(Human Interface Guidelines)中对此的介绍:
很少的情况下,即游戏等沉浸式 App,可能新需要用户定义屏幕边缘手势。这些手势要优先于系统定义的手势。第一次滑动调用 App 特定的手势,第二次滑动调用系统定义手势。
任一视图控制器的重写都是很简单的:
override func preferredScreenEdgesDeferringSystemGestures() -> UIRectEdge { return .top }
总结
iPhone X 需做特别考虑。
这对于 iOS 工程师而言是否只是轻描淡写,还是需要去维护和编码的另一个视图控制器?可能是两者的混合。如果我们熟悉软件开发的连续体系的话,那么我们就明白,新的 API= 时间流逝 + 新的生态系统。当前在智能手机领域,更切实的说法是:时间流逝 +Apple 的生态系统 = 新的硬件 = 新的 API。
我们面对的是加长的 iPhone,具有不同的显示分辨,并需要处理导航栏上的相机小点,以及底框附近的两点小横条。
下回分解。
查看英文原文: iPhone X: Dealing with Home Indicator
感谢覃云对本文的审校。
评论