3D Touch for iOS 10 适配指南

这就是 iOS SpringBoard 上用力点按 App Icon 弹出的快捷操作菜单了。此类菜单分为两类,静态和动态。

静态 action 被定义在 app 的 info.plist 文件中。定义之后,用户在安装了你的 app 后就可以生效使用。例如:

-UIApplicationShortcutItems

--Item 0

---UIApplicationShortcutItemType      String    com.company.app.XXX

---UIApplicationShortcutItemTitle    String    New Chat

---UIApplicationShortcutItemIconType  String    UIApplicationShortcutIconTypeMessage(system type)

最后的 ShortcutIconType 可以使用系统提供的一些类型。而关于 Item 总数的问题,除去 iOS 10 开始系统增加的 “分享” 项外,最多只能设置 4 个(包括动静态项全部)。顺带一提,貌似大多数 app 的做法都是一个静态项外加三个动态生成项。

与上面静态项所对应的就是 dynamic item,动态项是你的 App 在运行时创建的,所以只有在你的 app 第一次启动后才可以生成并可用。并且顺序上 dynamic item 是展示在 static item(看 action 列表展开的方向嘛,动态项会比静态项离手指更远)。但是动态项除了可以使用上面提到的系统提供的 icon 外,还可以使用自定义的 icon,以及通讯录中联系人的头像👦。举个例子:

let contactName = "RocZhang"

var contactIcon: UIApplicationShortcutIcon? = nil

// Make sure to request access to the user's contacts first

if CNContactStore.authorizationStatue(for: .contacts) == .authorized {

let predicate = CNContact.predicateForContacts(matchingName: contactName)

let contacts = try? CNContactStore().unifiedContacts(matching: predicate, keysToFecth: [])

if let contact = contacts?.first {

contactIcon = UIApplicationShortcutIcon(contact: contact)

}

}

// Fallback

let icon = contactIcon ??  UIApplicationShortcutIcon(type: .message)

// Create a Dynamic quick action using the icon

let type = "com.company.app.sendMessageTo"

let subtitle = "Send a message"

let shortcutItem1 = UIApplicationShortcutItem(type: type, localizedTitle: contactName, localizedSubTitle: subtitle, icon: icon)

// Repeat ...

let shortcutItems = [shortcutItem1, shortcutItem2, shortcutItem3]

// Register the Dynamic quick actions to display on the home Screen

UIApplication.shared.shortcutItems = shortcutItems

设置好这些快捷操作项后我们当然要处理相应点击后的操作,两种情况:

func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: Bool -> Void) {

let didHandle: Bool =  handle the quick action using shortcutItem

completionHandler(didHandle)

}

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

var performAdditionalHandling = true

if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem {

handle the quick action using shortcutItem

performAdditionalHandling = false

}

return performAdditionalHandling

}

关于上述的 Quick Actions,Apple 提供了一些建议:

每个 app 都应该提供 quick actions(我想这可能就是 iOS 10 系统全部加上 “分享” 的原因之一🤗)

好钢用在刀刃上(因为总数只有 4 个,所以 Apple 建议应该给具有高价值的任务创建快捷进入项)

确保你设置的项目是可被用户预知的

做好版本升级后依然能处理前一个版本生成的动态快捷项的准备

如果你还不太了解 peek pop 是什么,建议去看一下超炫酷的 iPhone 6s 发布时介绍 3D Touch 的视频。


简单说来,Peek & Pop 提供了一种可供用户快速预览和在内容之间导航的方式。


适配 Peek & Pop 非常简单,但首先需要了解一下,CocoaTouch 中把这两个动作先后称之为 Preview 和 Commit。

适配的过程可分为以下几步:

一,让 ViewController 遵循 UIViewControllerPreviewingDelegate:

// MARK: - UIViewControllerPreviewingDelegate Methods

extension ViewController: UIViewControllerPreviewingDelegate {

}

二,是把 ViewController 注册 Previewing:

override func viewDidLoad() {

super.viewDidLoad()

registerForPreviewing(with: self, sourceView: tableView)

}

三,实现 UIViewControllerPreviewingDelegate 中的 preview 和 commit 方法:

// MARK: - UIViewControllerPreviewingDelegate Methods

extension ViewController: UIViewControllerPreviewingDelegate {

func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation locatin: CGPoint) -> UIViewContrller? {

guard let indexPath = tableView.indexPathForRow(at: location) else { return nil }

let chatDetailViewController = ...

chatDetailViewController.chatItem = chatItem(at: indexPath)

let cellRect = tableView.rectForRow(at: indexPath)

let sourceRect = previewingContext.sourceView.convert(cellRect, from: tableView)

previewingContext.sourceRect = sourceRect

return chatDetailViewController

}

func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewContrller) {

show(viewControllerToCommit, sender: self)

}}


这里你可以自己决定是否要提供一些预览时的快捷操作。并不是最开始说的主屏幕上的快捷操作,而是这里的:

这里需要 override 一个 previewActionItems 的函数:

override func previewActionItems() -> [UIPreviewActionItem] {

let heart = UIPreviewAction(title: "", style: .default) { (action, viewController) in

}

return [heart]

}

其中的 style 除了 .default 还有 .selected 代表被选中,以及 .destructive 代表具有破坏性的操作。


同样,关于 Peek & Pop ,Apple 提供了一些建议:

可以被点击的内容应该要考虑支持 Peek & Pop (和上面那条 每个 app 都应该提供 quick actions 差不多,毕竟 3D Touch 用力点按之前用户并不知道会发生什么,有些可以响应 3D Touch 有些又不能就可能会让用户很不爽,久而久之就不愿意去使用 3D Touch 了)

不要在 previewing delegate 中花费太长的时间,因为是需要 peek 一下就显示出来,不能做太过费时的操作。


UIPreviewInteraction 似乎是用的比较少的,这是一个可以让我们的视图提供响应 3D Touch 交互动作的类。刚才提到 preview 和 commit,实际上这是使用 3D Touch preview 中包括的两个过程。由于从开始点按屏幕到响应 peek(preview 阶段结束) 再到响应 pop (commit 阶段结束),力度是有变化的。通过 UIPreviewInteraction 我们就可以获取当前用户点按力度分别在这两个阶段中的进度(0-1),这两个阶段的关系使用 API 官网中的一张图就可以表示清楚:

适配过程同样很简单,大致如下:

一,遵循 UIPreviewInteractionDelegate

extension xxViewController: UIPreviewInteractionDelegate

二,创建 UIPreviewInteraction 并设置 delegate

private var previewInteraction: UIPreviewInteraction?

override func viewDidLoad {

super.viewDidLoad()

previewInteraction = UIPreviewInteraction(view: view)

previewInteraction?.delegate = self

}

三,就可以通过代理方法获取到当前的进度,然后做你需要的事情了。比如:

func previewInteraction(_ previewInteraction: UIPreviewInteraction, didUpdatePreviewTransition  transitionProgress: CGFloat, ended: Bool) {

// Do something

}

因为通过 progress 获取到进度,所以我们可以通过这个值来驱动一些动画之类的,仔细想想这个应该会是蛮好玩的。


此外,session 228 的最后也提及了一下低层级力度 API ,在支持 3D Touch 或 Apple Pencil 的设备上,你可以获取到规范化的力度数据。关于这方面的内容,可以参见另一个 session: Leveraging Touch input on iOS.


下面是一些 session 中并没有提及的内容。

除去上面通过 UIViewControllerPreviewingDelegate 适配常见的 UITableView, UICollectionView 等的 peek 与 pop 操作,还有一种比较常见的场景是,我们希望在 3D Touch 发生在 cell 的每个部分上时,作出不同的响应(比如,3D Touch 了某个 feed 我们希望预览这个 feed 的详情,而点的时 feed 里的头像时,我们希望弹出的预览是 profile)。我们可以在上面的基础上进一步,比如:

public func viewContainsFormSuperview(with view: UIView, location: CGPoint) -> Bool {

let location = view.convert(location, from: self)

return view.bounds.contains(location)

}

public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {

guard let indexPath = tableView.indexPathForRow(at: location) else { return nil }

guard let cell = tableView.cellForRow(at: indexPath) as? xxCell else { return nil }

let avatarView = cell.avatarView

let location = avatarView.convert(location, from: tableView)

if avatarView.bounds.contains(location) {

let viewRect = tableView.convert(avatarView.frame, from: avatarView.superview)

previewingContext.sourceRect = viewRect

return ProfileViewController()

} else {

return nil

}

}

总结,session 中方提到的适配 3D Touch 主要三个方面 – 主屏幕快捷操作可以使用户直接跳转进对应的动作,Peek & Pop 允许用户快速预览并导航到内容,最后的 UIPreviewInteraction 也为 app 的交互提供了新的可能。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容