iOS 3D Touch开发(完整、详细版)
一、3D Touch 简介
在iOS 9中,新 iPhone 将第三维度添加到了用户界面。
- 用户现在可以用力摁下主屏按钮来快速调出应用提供的功能菜单。
- 在应用中,用户现在可以用力摁下视图以查看更多内容的预览并且快速访问一些功能。
本文主要讲解 3D Touch 各种场景下的开发方法,开发主屏幕应用 icon 上的快捷选项标签(Home Screen Quick Actions),静态设置 UIApplicationShortcutItem ,动态添加 UIApplicationShortcutItem,以及 Peek 和 Pop 的实现。
二、Home Screen Quick Actions
添加Home Screen Quick Actions有两种方式:
1、通过Plist文件静态设置;
2、通过代码动态添加。
两种方法的区别在于:通过Plist设置无需运行程序,也就是说在下载App后,不需要打开应用,就可以即可唤出Home Screen Quick Actions;而通过代码动态添加的,必须在第一次下载后打开App,才能出现Home Screen Quick Actions。
1. 通过Plist文件静态设置
在应用的 Info.plist 文件中添加UIApplicationShortcutItems
数组。
数组中添加字典,如下图所示:
其中,字典的Key有以下选项:
名称 | 说明 | 是否必须 |
---|---|---|
UIApplicationShortcutItemTitle | 标签的标题 | 必填 |
UIApplicationShortcutItemType | 标签的唯一标识 | 必填 |
UIApplicationShortcutItemIconType | 使用系统图标的类型,如搜索、定位、home等 | 可选 |
UIApplicationShortcutItemIconFile | 使用项目中的图片作为标签图标 | 可选 |
UIApplicationShortcutItemSubtitle | 标签副标题 | 可选 |
UIApplicationShortcutItemUserInfo | 字典信息,如传值使用 | 可选 |
第一个图片使用系统自带的,第二个是使用Assets.xcassets
文件夹中的图片,当完成设置后,效果如下:
2. 通过代码动态添加
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let item1 = UIApplicationShortcutItem(type: "2", localizedTitle: "code add item1", localizedSubtitle: "code add subtitle", icon: UIApplicationShortcutIcon(templateImageName: "shortcut_scorecard"), userInfo: nil)
let item2 = UIApplicationShortcutItem(type: "3", localizedTitle: "code add item2", localizedSubtitle: nil, icon: UIApplicationShortcutIcon(type: .add), userInfo: nil)
application.shortcutItems = [item1, item2]
return true
}
效果如下:
注意:目前Home Screen Quick Actions最多只能添加4个。
无论用哪种方式添加,当点击item后,都会调用
AppDelegate
中的application(_:performActionFor:completionHandler:)
方法:
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
print(shortcutItem.type) // 通过type来判断是点击了哪一个按钮
}
三、Peek and Pop(预览与跳转)
- Peek:轻按,屏幕视图就会过渡到Peek,一个你设置的用来展示更多内容的视图-就像Mail app做的一样。如果用户这时结束了触碰,Peek就会消失并且应用回到交互开始之前的状态。当你用力按下屏幕按到一定程度时,系统会弹出一个预览视图,这个过程就称之为Peek。
- Pop:或者这个时候,用户可以在peek界面上更用力按下来跳转到使用peek呈现的视图,这个过渡动画会使用系统提供的pop过渡。pop出来的视图会填满你应用的根视图并显示一个返航按钮可以回到交互开始的地方。再用力按下去就会展开到预览视图的控制器,这过程就是Pop。
实现步骤:
- 注册预览代理
// Registers a view controller to participate with 3D Touch preview (peek) and commit (pop).
@available(iOS 9.0, *)
// 第一个参数:代理
// 第二个参数:哪个view执行Peek
open func registerForPreviewing(with delegate: UIViewControllerPreviewingDelegate, sourceView: UIView) -> UIViewControllerPreviewing
- 遵守协议:
UIViewControllerPreviewingDelegate
- 实现协议方法
// If you return nil, a preview presentation will not be performed
@available(iOS 9.0, *)
public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController?
@available(iOS 9.0, *)
public func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController)
接下来用一个tableView的例子🌰来说明,实现以下效果:
代码如下:
import UIKit
class ViewController: UIViewController {
fileprivate lazy var array = [String]()
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
for i in 0...20 { array.append("第\(i)行") }
// 注册Cell
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
// 判断系统版本,必须iOS 9及以上,同时检测是否支持触摸力度识别
if #available(iOS 9.0, *), traitCollection.forceTouchCapability == .available {
// 注册预览代理,self监听,tableview执行Peek
registerForPreviewing(with: self, sourceView: tableView)
}
}
}
// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = array[indexPath.row]
return cell
}
}
// MARK: - UIViewControllerPreviewingDelegate
@available(iOS 9.0, *)
extension ViewController: UIViewControllerPreviewingDelegate {
func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
// 模态弹出需要展现的控制器
showDetailViewController(viewControllerToCommit, sender: nil)
// 通过导航栏push需要展现的控制器
// show(viewControllerToCommit, sender: nil)
}
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
// 获取indexPath和cell
guard let indexPath = tableView.indexPathForRow(at: location), let cell = tableView.cellForRow(at: indexPath) else { return nil }
// 设置Peek视图突出显示的frame
previewingContext.sourceRect = cell.frame
let vc = DetalViewController()
// 返回需要弹出的控制权
return vc
}
}
在实际开发中,你可能会用到 UIView中的坐标转换 的几个方法(可跳过不看):
// 将像素point由point所在视图转换到目标视图view中,返回在目标视图view中的像素值
open func convert(_ point: CGPoint, to view: UIView?) -> CGPoint
// 例如:point2 = view1.convert(point1, toView: view2):把point1从view1的坐标系中,转换到view2的坐标系中
// 将像素point从view中转换到当前视图中,返回在当前视图中的像素值
open func convert(_ point: CGPoint, from view: UIView?) -> CGPoint
// 例如:point2 = view1.convert(point1, from: view2):把point1从view2的坐标系中,转换到view1的坐标系中
// 将rect由rect所在视图转换到目标视图view中,返回在目标视图view中的rect
open func convert(_ rect: CGRect, to view: UIView?) -> CGRect
// 例如:rect2 = view1.convert(rect1, toView: view2):把rect1从view1的坐标系中,转换到view2的坐标系中
// 将rect从view中转换到当前视图中,返回在当前视图中的rect
open func convert(_ rect: CGRect, from view: UIView?) -> CGRect
// 例如:rect2 = view1.convert(rect1, toView: view2):把rect1从view2的坐标系中,转换到view1的坐标系中
四、Peek快速选项
如果用户一直保持触摸,可以向上滑动Peek视图,系统会展示出你预先设置和peek关联的peek快速选项。
每一项peek快速选项都是你应用中的深度链接。当peek快速选项出现后,用户可以停止触摸而且peek会停留在屏幕中。用户可点击一个快速选项,唤出相关链接。
在本例中,实现以上效果,只需要在
DetalViewController
中添加以下代码即可:
import UIKit
class DetalViewController: UIViewController {
@available(iOS 9.0, *)
lazy var previewActions: [UIPreviewActionItem] = {
let a = UIPreviewAction(title: "这是一个default按钮", style: .default, handler: { (action, vc) in
// 这里实现点击按钮事件处理
})
let b = UIPreviewAction(title: "这是一个destructive按钮", style: .destructive, handler: { (action, vc) in
// 这里实现点击按钮事件处理
})
return [a, b]
}()
@available(iOS 9.0, *)
override var previewActionItems: [UIPreviewActionItem] {
return previewActions
}
}
最后注意:以上全部功能必须要求为 iPhone 6s 及以上机型,并且是 iOS 9 及以上,才有效果,模拟器可以看到效果,不需要安装任何插件,但必须要有触摸盘才行,在触摸盘上的点击就是3D Touch。