PPPan's 平凡之路

做一个互联网内容的贡献者

PPPan's 平凡之路 一个技术博客覆盖范围包括 iOS Objecticve-C Swift Xcode 等


让你的应用更好地利用 3D Touch - WWDC 2016 Session 228 笔记

我们回首 iPhone 的历程,不禁感叹它是如何不断改变我们对手机的认知的。从触屏改变手机的定义开始,距离传感器、光线传感器,到三轴陀螺仪、GPS、运动传感器、再到指纹。这些功能一步步地拓展 iPhone 的能力,不断地改变着我们的生活。

iPhone 6s 再次增加了新的功能 – 3D Touch。 它为iOS 设备的操作增加了另一个维度的能力,为用户提供了另一个操作体验,甚至是改变了用户的操作习惯。随着设备的普及和软件的跟进,3D Touch 也许会像指纹解锁一样,不可或缺的存在。

关于 3D Touch, Apple 主要为我们提供了两种封装好的功能:

  1. 主屏幕快捷菜单 ( Home screen quick action)
  2. 预览 ( peek and pop )

Home screen quick action

Home screen quick action 的表现形式非常简单,利用 3D Touch 按主屏幕图标,则会弹出该App 的快捷菜单。点击快捷菜单则会唤起app,并进入到相对应的功能。

主屏幕快捷菜单分为静态动态的两种。静态即固定的菜单。动态则代表菜单项是可变的。例如激活一款聊天软件的 Quick Action,出现三个你联系最频繁的人。这便需要动态菜单来实现。

当然,他们的开发也同表现形式一样简单。

静态快捷菜单

创建一个静态的快捷菜单,只需要简单地在Info.plist中添加一个 UIApplicationShortcutItems 的 Array 即可。

你可以为快捷菜单指定标题、系统或自定义的icon等,所有这些,都只需要在 UIApplicationShortcutItems 下添加一些key 和 value 即可。这些key可以在这里找到。

iOS 10 中,快捷菜单同时可以附带显示一个 Widget ,也就是以前的 App Extension。如果你提供了多个 Extension,可以在 Info.plist 中添加 UIApplicationShortcutWidget,其 Value 代表快捷菜单中展示的 Extension 的 Bundle id.

动态快捷菜单

创建一个动态的快捷菜单也非常容易,只需要初始化并配置 UIApplicationShortcutItem, UIMutableApplicationShortcutItemUIApplicationShortcutIcon 这三个类,并将其添加到 AppDelegate 中的 shortcutItems 属性即可。

值得一提的是,静态快捷菜单在 App 被安装的那一刻就可用了。区别于静态菜单,动态快捷菜单只有当第一次启动后才可用。

处理快捷菜单的点击

这里分当 App 被 kill当 App 未被 kill两种情况。

当 App 仍然在运行的情况下,点击快捷菜单会触发以下方法:

1
2
3
4
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
// 做你想做的事
// 最后不要忘了调用completionHandler()
}

当 App 被 kill 的情况下,点击快捷菜单会触发以下方法:

1
2
3
4
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// 通过 UIApplicationLaunchOptionsShortcutItemKey 在 launchOptions 中取得 UIApplicationShortcutItem
// 然后做你想做的事
}

Peek and Pop

macOS 中按下空格键即可预览各种各样的文件。通过 3D Touch 现在它也来到了 iOS 中。这一操作被 Apple 很形象地称为 Peek and Pop

Peek and Pop 将传统的 Push 操作分为了两步,当你的手指按压某行列表,背景开始模式模糊,然后出现一个预览界面,然后继续增加压力,伴随着俏皮的弹性动画,下一个界面呈现在你眼前。在 API 中,这两部分别被称为 PreviewCommit

为你的 App 添加 Peek and Pop

为 App 添加 Peek and Pop 非常简单,只要遵循以下几步:

  1. 让需要预览的 ViewController 遵循 UIViewControllerPreviewingDelegate 协议
  2. 调用 registerForPreviewing(with:sourceView:) 注册该ViewController
  3. 在 preview 代理方法中提供一个预览的ViewController,并设置好 context 的 sourceRect.
  4. 在 commit 代理方法中,直接调用 show(_:sender:) 即可。

编码的过程中,要注意检查 3D Touch 的可用性。 因为这项功能同定位一样,用户是可以将其关闭的!为了保证所有的用户都能使用到你 App 的功能,应当依据 3D Touch 的可用性,来编写不同的代码。 当 3D Touch 可用时,那就用上着炫酷的新功能吧!如果不支持 3D Touch,我们还有另外一个备选方案 – UILongPressGestureRecognizer.

另外,不要在 UIViewControllerPreviewingDelegate 中做非常耗时的工作,那会造成界面卡顿。

合理地利用 Peek and Pop 可以为用户带来无缝的体验。 例如,下一个界面会有大量的初始化工作,在preview的过程中,你可以预先加载并提供部分预览(同时也为下一个界面做准备),记住不要卡住主线程,等到 Pop 时,界面已经初始化好了。这为用户提供了一个非常好的体验。

Preview Action

在 Preview 的过程中,用户可以上滑来唤出类似 Action Sheet 的菜单。实现这一功能只需要重写 ViewController 中的 previewActionItems() -> [UIPreviewActionItem]方法即可。
系统提供了和 UIAlertAction 非常类似的 UIPreviewAction,来实现 UIPreviewActionItem

与 Action Sheet 不同的是,系统提供了 UIPreviewActionGroup 类,实现子菜单的功能。

在开发 Peek and Pop 的过程中,请记住以下原则

  1. 让合适的内容支持 Peek and Pop (不要滥用这项特性)
  2. 始终返回相同的预览界面 (保持一致性和可预测性)
  3. 不要在preview代理方法中花太多的时间
  4. 为 context 设置正确的 sourceRect

UIPreviewInteractionDelegate

iOS 10 中为我们带来了全新的 API,可以让你自定义 Peek and Pop 操作。 我们只需要让某个对象遵循 UIPreviewInteractionDelegate 协议,并在相对于的代理方法中做我们想做的事情就可以了。

UIPreviewInteractionDelegate 一共包含以下四个方法:

1
2
3
4
5
6
// 必须实现
// 这个方法可以让你精细地控制从按压到 Preview 触发过程中发生的事
// 配合 UIViewControllerTransitioningDelegate ,可以做类似新郎微博的 + 号菜单效果。

@available(iOS 10.0, *)
public func previewInteraction(_ previewInteraction: UIPreviewInteraction, didUpdatePreviewTransition transitionProgress: CGFloat, ended: Bool) // transitionProgress ranges from 0 to 1
1
2
3
4
// 这个方法也是必须实现的,用来处理一些 Interaction 被打断情况
// 比如说:接到一个电话。
@available(iOS 10.0, *)
public func previewInteractionDidCancel(_ previewInteraction: UIPreviewInteraction)
1
2
3
4
5
// 你可以用这个方法来控制 Delegate 的触发。
// return false 则不会触发其他代理方法

@available(iOS 10.0, *)
optional public func previewInteractionShouldBegin(_ previewInteraction: UIPreviewInteraction) -> Bool
1
2
3
// 用这个方法来精细地控制 Preview -> Commit 阶段
@available(iOS 10.0, *)
optional public func previewInteraction(_ previewInteraction: UIPreviewInteraction, didUpdateCommitTransition transitionProgress: CGFloat, ended: Bool)

UIPreviewInteractionDelegate 让 3D Touch 变得更灵活了。如果我们希望 iOS 9 也支持这样的功能怎么办呢? 不用担心,Apple 还提供了更底层的 API。

Low Level Force API

在 UITouch 中有两个关键的属性为我们提供了压力相关数据的存储。

1
2
@property(nonatomic,readonly) CGFloat force NS_AVAILABLE_IOS(9_0);
@property(nonatomic,readonly) CGFloat maximumPossibleForce NS_AVAILABLE_IOS(9_0);

force 代表当前 Touch 的压力系数,默认是 0.0 ~ 1.0。你也可以通过 maximumPossibleForce 来调整 force 的上限,来为其提供更宽的变化范围。

上面的 API 仅在支持 3D Touch 和 Apple Pencil 的设备上可用,所以在使用前,不要忘了检查 UITraitCollection 中的forceTouchCapability,来确定以上 API 是否可用。

为什么将 forceTouchCapability 放在 UITraitCollection 中?

因为 Size Classes 将设备抽象成了 Size.

3D Touch 的最佳实践

  1. 每个 app 都应该提供 quick actions。其简创建简单,却能带来巨大的价值。
  2. 为高价值的任务提供一个快速的入口。
  3. 很重要的一点,你需要保证 quik action 的可预测性,用户们如果经常使用它,却发现每一次的结果都不一样,就会因此而困惑。
  4. 即使你做了 3D touch 用户也可以禁用这一项功能,因此不能过度依赖这项特性来做一些必须的事情,同时也要准备好备选方案。

总结

  • 主页的 quick action 可以让你的app 直接进入相关行为。
  • Peek and Pop 允许你快速预览内容,并导航到对应界面。
  • UIPreviewInteraction 让你可以更精细地控制 3D Touch。
  • 用户们期望你支持 3D touch。

参考资料

WWDC 2016 Sssion 228 - A Peek at 3D Touch

WWDC 2016 Sssion 228 - A Peek at 3D Touch Presentation Slides

Adopting 3D Touch

Apple WWDC 示例代码: AppChat

Newer Post

微信 URL Schemes

DEPRECATED2016年09月18日更新: 在 微信 6.3.25 版本中,所有外部唤起 URL 的方式均无法打开对应页面。未来这些 url 都只能在微信内部的浏览器使用了。 年初的动态化分享热潮里,基本上都提到了 URL 跳转,天猫称为统跳协议,蘑菇街称为 URL 路由。其实很多大厂都默默 …

继续阅读
Older Post

电池电量与低电量模式

优步最近公布的数据显示,当电池快没电的时候,人们更愿意接受溢价。同时,优步也表明他们并没有利用电量相关的数据来设置溢价规则。换句话说,我们在开发App的时候,可以通过电池电量来”提升用户体验”。 电池状态与电量UIDevice 中有三个电池相关的属性: 123@property(nonatomic, …

继续阅读