一、简介
3D Touch 是在 6s 以上手机才可以使用的一个功能,通过按压屏幕,做出相应的响应事件,交互性特别好。
它在项目中,主要有以下两个应用:
- 通过按钮屏幕上的APP图标,弹出选项菜单,可
快速进入
APP里面的某个页面- 在APP内部,通过按压某个控件,
预览
下一页的内容,继续按压可进入详情
二、应用图标按压快速进入APP
具体效果图
ShortcutItem
功能用于实现对 应用图标 的3D Touch 操作
它可通过 静态、动态 两种方式添加到项目中
1. 静态添加
在 info.plist
中添加 UIApplicationShortcutItems
关键字,并做如下配置
UIApplicationShortcutItems : 表示选项列表
UIApplicationShortcutItemType: 每个 item 的唯一标识
UIApplicationShortcutItemTitle: 标题
UIApplicationShortcutItemSubtitle: 子标题(可不填)
UIApplicationShortcutItemIconType: 图标(可使用自定义图标)
2.动态添加 (推荐使用)
除了在 plist 文件中配置外,我们还可以在 appdelegate 方法中通过函数来创建 UIApplicationShortcutItem
如下:
UIApplicationShortcutIcon *searchIcon = [UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeSearch];
UIApplicationShortcutItem *searceItem = [[UIApplicationShortcutItem alloc]
initWithType:@"com.268.search"
localizedTitle:@"搜索"
localizedSubtitle:nil
icon:searchIcon
userInfo:nil];
上面创建的图标 UIApplicationShortcutIcon
是使用系统自带的,也可以自己创建,如下
// 这里可以自定义图标,不使用系统的 icon, 推荐尺寸是 35*35,单色(因为系统会把图片渲染成灰黑色)
UIApplicationShortcutIcon *livingIcon = [UIApplicationShortcutIcon iconWithTemplateImageName:@"我的_我的直播"];
每一个 item 都是一个选项,最后加入到 application.shortcutItems
数组即可
application.shortcutItems = @[searceItem,livingItem,courseItem,messageItem];
由于 UIApplicationShortcutItem 创建方法是在 iOS 9 以后的,所以为了防止之前的机型调用此方法崩溃,所以在调用上述方法前,最好加一个验证
// 监测 3D Touch 是否可用
if ([self respondsToSelector:@selector(traitCollection)]) {
if ([self.window.traitCollection respondsToSelector:@selector(forceTouchCapability)]) {
if (self.window.traitCollection.forceTouchCapability != UIForceTouchCapabilityAvailable) {
return;
}
}
}
3. UIApplicationShortcutItem 回调方法
当点击图标快捷选项时,会调用 AppDelegate 中的回调方法
-(void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
我们在里面做相应操作即可,如下
-(void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
ChXDemoViewController *demoVC = [[NSClassFromString(@"ChXDemoViewController") alloc] init];
if ([shortcutItem.type isEqualToString:@"com.268.search"]) { // 搜索
// UIViewController *searchVc = [[NSClassFromString(@"SearchViewController") alloc] init];
// [self.mainController pushViewController:searchVc animated:YES];
demoVC.titleName = @"进入了搜索页面";
} else if ([shortcutItem.type isEqualToString:@"com.268.message"]) { // 系统消息
// if ([USERID integerValue] != 0) {
// UIViewController *nextVc = [[NSClassFromString(@"NoticeViewController") alloc]init];
// [self.mainController pushViewController:nextVc animated:NO];
// } else {
//// [self showLoginViewController:self.mainController];
// }
demoVC.title = @"进入消息";
} else if ([shortcutItem.type isEqualToString:@"com.268.course"]) {
// if ([USERID integerValue] != 0) {
// UIViewController *nextVC = [[NSClassFromString(@"MyCourseTableViewController") alloc] init];
//// [self.mainController pushViewController:nextVC animated:NO];
// } else {
//// [self showLoginViewController:self.mainController];
// }
demoVC.title = @"进入课程";
} else if ([shortcutItem.type isEqualToString:@"com.268.living"]) {
// if ([USERID integerValue] != 0) {
// UIViewController *nextVC = [[NSClassFromString(@"ChXMyLiveController") alloc] init];
//// [self.mainController pushViewController:nextVC animated:NO];
// } else {
//// [self showLoginViewController:self.mainController];
// }
demoVC.title = @"进入直播";
} else {
}
// 这里的页面跳转是一个例子,具体根据实际项目做,意思是先获取根视图,新建要跳转的控制器,然后prensen 或 push 页面即可
[self.window.rootViewController presentViewController:demoVC animated:YES completion:nil];
}
三、APP 内部预览 peek & pop
- 要使用 预览(peek)功能,需要在当前控制器中遵守
UIViewControllerPreviewingDelegate
协议
@interface TableViewController_A ()
@end
-
实现用力按下 cell, 弹出预览,如图
图片.png
首先需要 注册 cell 可以预览, 在 cellForRowAtIndexPath
中输入下面代码
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellId" forIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:@"第%ld行",(long)indexPath.row];
// 注册需要实现 Touch 效果的view, 这里是 用力按下cell,弹出预览小视图,同时上滑底部出现若干个选项(peek功能)
// 首先判断设备系统是否支持,否则会崩溃
if ([self respondsToSelector:@selector(traitCollection)]) {
if ([self.traitCollection respondsToSelector:@selector(forceTouchCapability)]) {
if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
[self registerForPreviewingWithDelegate:self sourceView:cell];
}
}
}
return cell;
}
然后实现 UIViewControllerPreviewingDelegate
中的代理方法 commitViewController
即跳到下一页了
// 继续用力按下进入下一页(pop)
- (void)previewingContext:(id)previewingContext commitViewController:(UIViewController *)viewControllerToCommit {
[self presentViewController:viewControllerToCommit animated:YES completion:nil];
}
如果你希望,在预览的图向上滑动时,有菜单选项,如下图
你只需要两步:
- 在当前控制器创建一个
UIPreviewAction
的数组,并把相应的处理事件写在里面
- (NSArray *)recordPreviewActionItems {
if (!_recordPreviewActionItems) {
UIPreviewAction *action1 = [UIPreviewAction actionWithTitle:@"删除" style:UIPreviewActionStyleDestructive handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
// [self deleteCellWithIndexPath:self.previewCellIndexPath];
NSLog(@"删除");
}];
UIPreviewAction *action2 = [UIPreviewAction actionWithTitle:@"返回" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
NSLog(@"预览返回");
}];
_recordPreviewActionItems = @[action1,action2];
}
return _recordPreviewActionItems;
}
- 赋值给下一个控制器
在创建 DetailViewController 控制器的代码下面,给recordPreviewActionItems 属性赋值
detailVc.recordPreviewActionItems = self.recordPreviewActionItems;
DetailViewController 里面需要重写 previewActionItems
方法
@property (nonatomic, strong) NSArray *recordPreviewActionItems;
#pragma mark - **************** 3D Touch 预览时下方快捷菜单
- (NSArray> *)previewActionItems {
return self.recordPreviewActionItems;
}
好了,方法就是这些了,下面就介绍下 Swift 中的写法吧
Swift 中的代码
其实,就是把 OC 的翻译一下 。。。
AppDelegate.swift:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
configure3DTouch(application: application)
return true
}
/// 配置 3D Touch
///
/// - Parameter application: UIApplication
func configure3DTouch(application: UIApplication) {
// 判断当前设置是否支持 3D Touch
if #available(iOS 9.0, *) {
if window?.traitCollection.forceTouchCapability != UIForceTouchCapability.available {
print("3D touch 不可用")
return;
}
} else {
print("3D touch 不可用")
return
}
// 添加 item
let searchIcon = UIApplicationShortcutIcon.init(type: .search)
let searchItem = UIApplicationShortcutItem.init(type: "com.268.search",
localizedTitle: "搜一搜",
localizedSubtitle: "点击进入搜索",
icon: searchIcon,
userInfo: nil)
let livingIcon = UIApplicationShortcutIcon.init(templateImageName: "我的_我的直播")
let livingItem = UIApplicationShortcutItem.init(type: "com.268.living",
localizedTitle: "直播",
localizedSubtitle: nil,
icon: livingIcon,
userInfo: nil)
application.shortcutItems = [searchItem, livingItem]
}
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
let rootVc = window?.rootViewController
let nextVc = DemoViewController()
// 根据 type 唯一标识进行判断跳转
switch shortcutItem.type {
case "com.268.search":
nextVc.name = "搜索页面"
default:
nextVc.name = "直播页面"
}
rootVc?.present(nextVc, animated: true, completion: nil)
}
列表 TableViewController_A:
class TableViewController_A: UITableViewController ,UIViewControllerPreviewingDelegate{
/// 详情页预览选项数组
private lazy var detailPreviewActionItems = [UIPreviewAction]()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.green
tableView.register(NSClassFromString("UITableViewCell"), forCellReuseIdentifier: "cellId")
configureDetailPreviewItems()
}
func configureDetailPreviewItems() {
let action1 = UIPreviewAction.init(title: "删除",
style: .destructive) { (_, _) in
print("删除了~")
}
let action2 = UIPreviewAction.init(title: "返回",
style: .default) { (_, _) in
print("返回了~")
}
detailPreviewActionItems = [action1, action2]
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 10
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath)
cell.textLabel?.text = "第 \(indexPath.row) 行"
// 判断是否支持 3D Touch
if #available(iOS 9.0, *) {
if traitCollection.forceTouchCapability == UIForceTouchCapability.available {
// 注册 Peek & Pop 功能
registerForPreviewing(with: self, sourceView: cell)
}
}
return cell
}
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
// 1. 获取当前按压 cell 所在的行
guard let cell = previewingContext.sourceView as? UITableViewCell else {
return UIViewController()
}
let indexPath = tableView.indexPath(for: cell) ?? IndexPath(row: 0, section: 0)
// 2. 设置预览界面
let nextVc = DemoViewController()
nextVc.name = "第 \(indexPath.row) 行过来的"
nextVc.items = detailPreviewActionItems
return nextVc
}
func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
show(viewControllerToCommit, sender: self)
}
}
详情 DemoViewController:
class DemoViewController: UIViewController {
@IBOutlet weak var titleLabel: UILabel!
var items: [UIPreviewAction]?
/// 标题
var name: String?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.groupTableViewBackground
titleLabel.text = (name ?? "")
}
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
dismiss(animated: true, completion: nil)
}
override var previewActionItems: [UIPreviewActionItem] {
return items ?? []
}
}