3D Touch 在 OC 、Swift 中的实际应用

一、简介

3D Touch 是在 6s 以上手机才可以使用的一个功能,通过按压屏幕,做出相应的响应事件,交互性特别好。
它在项目中,主要有以下两个应用:

  1. 通过按钮屏幕上的APP图标,弹出选项菜单,可快速进入APP里面的某个页面
  2. 在APP内部,通过按压某个控件,预览下一页的内容,继续按压可进入详情

二、应用图标按压快速进入APP

具体效果图


图片.png
图片.png

ShortcutItem 功能用于实现对 应用图标 的3D Touch 操作
它可通过 静态、动态 两种方式添加到项目中

1. 静态添加

info.plist 中添加 UIApplicationShortcutItems 关键字,并做如下配置

图片.png

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

  1. 要使用 预览(peek)功能,需要在当前控制器中遵守 UIViewControllerPreviewingDelegate 协议
@interface TableViewController_A ()
@end
  1. 实现用力按下 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];
}

如果你希望,在预览的图向上滑动时,有菜单选项,如下图


图片.png

你只需要两步:

  1. 在当前控制器创建一个 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;
}
  1. 赋值给下一个控制器
    在创建 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 ?? []
    }
    
}

你可能感兴趣的:(3D Touch 在 OC 、Swift 中的实际应用)