如何控制 iPhone X 的 Home Indicator

阅读数:1963 2017 年 11 月 16 日

话题:语言 & 开发

本文最初发布于 Medium 网站,原作者Jordan Morgan。本文经授权由 InfoQ 中文站翻译并分享。

我们每个人都应该听说了,Apple 发布了 iPhone X。随之而来的,是新推出的自动隐藏在手机屏幕底部的一个横条,官方称其为“Home 指示键”。它唤起了用户对 iPhone 物理 Home 按钮的怀旧感。

对于消费者而言,这意味着在硬件和软件上的一个新奇迹,每次预定时需要投入更多的钱。但是对于很多开发人员而言,这意味着应如何去处理这个鬼东西。感谢上帝,答案非常简单。

本周,我们将介绍 Apple 已为我们给出了的 Home 指示键处理技术。

首先

并非每天都有新视频随硬件发布,但这恰恰是在此之后发生的事情:



在 Twitter 上查看图片

在“为 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 函数几乎相同。

更新:读者问题解答

Fabian Kuenzel问:

新的 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 的safeAreaLayoutGuideAPI.

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,因此在实际使用之前我不做任何评论。

Will Kampmann问:

你是否了解,对于游戏等全屏 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

感谢覃云对本文的审校。