iOS之3D Touch

一、前言

3D Touch 是一种立体触控技术,被苹果称为新一代多点触控技术,是在Apple Watch 上采用的 Force Touch,屏幕可感应不同的感压力度触控。 3D Touch,苹果 iPhone 6s 的新功能,看起来类似 PC 上的右键。有 PeekPop 两种新手势。此功能的发布将手机屏幕的的操作坐标由 xy 轴扩大至 z 轴,增加了整整一个维度(即相对于多点触摸在平面二维空间的操作,3D Touch 技术增加了对力度和手指面积的感知,可以通过长按快速预览/查看你想要的短信/图片/超链接等内容,PeekPop 手势的响应时间可迅捷到 10ms15ms),这在屏幕时代属于非常伟大的创新。

demo地址

二、苹果的3D Touch主要呈现方式

2.1、按功能划分,有三种:
  • 主屏交互(Home Screen Interaction);
  • 预览和跳转(Peek and Pop);
  • LivePhoto

注:本文主要介绍一下前两种用法。

2.2、按3D Touch功能使用的位置划分,有两种:
  • 手机桌面,效果图如下:

    微信

    京东

  • 应用内,效果图如下:

    微信

三、使用

3.1、主屏交互(Home Screen Interaction);

所谓的主屏交互也就是在手机的桌面,用手指按压应用图标,生成的几个快捷操作按钮的,效果图如下:


主屏交互的按钮有指定的模型类:UIApplicationShortcutItem,添加 UIApplicationShortcutItem 有两种方式:静态添加和动态添加。

静态添加动态添加区别:静态设置是在应用安装的时候完成加载的,而动态设置需要在运行到对应代码时(runtime) 才加载,所以同时有静态加载的 Item 和动态加载的 Item时,静态加载的 Item 会排在前面。

3.1.1、静态添加UIApplicationShortcutItem

静态添加 UIApplicationShortcutItem 方式主要是在工程的 info.plist 文件中添加相关的属性,如下图:

// 数组中的元素就是上图中的快捷选项标签
UIApplicationShortcutItems

// 标签标题(必填)
UIApplicationShortcutItemTitle

// 标签的唯一标识 (必填)
UIApplicationShortcutItemType

// 使用系统图标的类型,如搜索、定位、home等(可选)
UIApplicationShortcutItemIconType

// 使用项目中的图片作为标签图标 (可选)
UIApplicationShortcutItemIconFile

// 标签副标题 (可选)
UIApplicationShortcutItemSubtitle

// 字典信息,如传值使用 (可选)
UIApplicationShortcutItemUserInfo
3.1.2、动态添加UIApplicationShortcutItem

UIApplicationShortcutItem 可以看作是 3D Touch 点击后,弹出菜单每行对应的模型,一行对应一个 UIApplicationShortcutItem 对象。

动态添加时就是我们可以通过生成 UIApplicationShortcutItem 对象数组,添加给 UIApplication 单例对象。

UIApplicationShortcutItem 初始方法如下:

/**
 @param type item的唯一标识(必填)
 @param localizedTitle 是item的标题(必填)
 @param localizedSubtitle 是item的副标题(可选)
 @param icon 是item的图标(可选)
 @param userInfo 是item所包含的信息,类型是字典(可选)
 */
- (instancetype)initWithType:(NSString *)type 
              localizedTitle:(NSString *)localizedTitle 
           localizedSubtitle:(nullable NSString *)localizedSubtitle 
                        icon:(nullable UIApplicationShortcutIcon *)icon
                    userInfo:(nullable NSDictionary> *)userInfo;

初始化生成 UIApplicationShortcutItem 对象这一步操作一般都是在 AppDelegate 类的方法里处理的,方法如下:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

动态添加时,首先要判断手机是否支持 3D Touch,代码如下:

if (self.window.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
// TODO - 创建3DTouch模型
}

生成 UIApplicationShortcutItem 对象数组,添加给 UIApplication 单例对象,示例代码如下:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 首先判断是否支持3D Touch
    if (self.window.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
        // 创建3D Touch模型
        [self setup3DTouch];
    }
    
    return YES;
}

- (void)setup3DTouch {
    NSMutableArray *shortcutItems = (NSMutableArray *)[UIApplication sharedApplication].shortcutItems;
    
    UIApplicationShortcutItem *shoreItem1 = [[UIApplicationShortcutItem alloc] initWithType:@"com.xw.test1" localizedTitle:@"test1" localizedSubtitle:@"sub test1" icon:[UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeAdd] userInfo:nil];
    UIApplicationShortcutItem *shortItem2 = [[UIApplicationShortcutItem alloc] initWithType:@"com.xw.test2" localizedTitle:@"test2" localizedSubtitle:@"sub test2" icon:[UIApplicationShortcutIcon iconWithTemplateImageName:@"like"] userInfo:nil];

    [shortcutItems addObject:shoreItem1];
    [shortcutItems addObject:shortItem2];
    
    [UIApplication sharedApplication].shortcutItems = shortcutItems;
}

在使用动态添加 UIApplicationShortcutItem 时,可以使用自定义的图标(官方推荐一倍图使用 35x35 ),也可以使用系统图标,如下:

typedef NS_ENUM(NSInteger, UIApplicationShortcutIconType) {
    UIApplicationShortcutIconTypeCompose,
    UIApplicationShortcutIconTypePlay,
    UIApplicationShortcutIconTypePause,
    UIApplicationShortcutIconTypeAdd,
    UIApplicationShortcutIconTypeLocation,
    UIApplicationShortcutIconTypeSearch,
    UIApplicationShortcutIconTypeShare,
    UIApplicationShortcutIconTypeProhibit       NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeContact        NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeHome           NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeMarkLocation   NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeFavorite       NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeLove           NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeCloud          NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeInvitation     NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeConfirmation   NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeMail           NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeMessage        NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeDate           NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeTime           NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeCapturePhoto   NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeCaptureVideo   NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeTask           NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeTaskCompleted  NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeAlarm          NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeBookmark       NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeShuffle        NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeAudio          NS_ENUM_AVAILABLE_IOS(9_1),
    UIApplicationShortcutIconTypeUpdate         NS_ENUM_AVAILABLE_IOS(9_1)
} API_AVAILABLE(ios(9.0)) API_UNAVAILABLE(tvos) API_UNAVAILABLE(macos);
3.1.3、监听主屏交互按钮的点击事件

设置好主屏交互的 item 后,我们剩下要做的就是在 app 内监听 item 的点击事件,此时需要用到之前设置 UIApplicationShortcutItemType 的唯一标示符,通过唯一标示符来判断用户点击了哪个 item,代码如下:

- (void)application:(UIApplication *)application 
performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem 
completionHandler:(void (^)(BOOL))completionHandler {
    // 不管APP在后台还是进程被杀死,只要通过主屏快捷操作进来的,都会调用这个方法
    NSLog(@"\n\r localizedTitle:%@ \n\r type:%@", shortcutItem.localizedTitle, shortcutItem.type);
}

3.2、预览和跳转

Peek and Pop 在操作上是使用一定力度按压屏幕,触发 Peek 操作;在 Peek 状态下,上划唤出 Peek 快速操作( UIPreviewAction );Peek 状态下,再次用更大力度按压屏幕,转场到预览的控制器,效果如下图:

3.2.1、Peek and Pop使用的相关说明
  • UIViewControllerPreviewingDelegate:触发 3D Touch 的类,需要遵循该协议,并要实现协议中的方法;
  • UIPreviewActionPeek 状态下,上划唤出快速操作选项实例;
  • previewActionItemsPeek 状态下,上划唤出快速操作选项实例数组。在要被 Pop 出的控制器中覆写 get 方法,返回一个 UIPreviewAction 数组。
3.2.2、Peek and Pop使用步骤

3.2.2.1、遵循协议,并注册代理
遵循 UIViewControllerPreviewingDelegate 协议,并在遵循 UIViewControllerPreviewingDelegate 协议的控制器中调用方法,方法如下:

- (id )registerForPreviewingWithDelegate:(id)delegate sourceView:(UIView *)sourceView;
  • 遵循协议
@interface ViewController () 
@end
  • 注册代理,并传入响应 3D Touch 的视图,下面是部分代码:
// 判断是否支持3D Touch
    if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
        // 注册代理,并传入响应3D Touch的视图
        [self registerForPreviewingWithDelegate:self sourceView:cell];
    }

3.2.2.2、 实现UIViewControllerPreviewingDelegate协议的两个方法

  • 当系统检测到 3D Touch 时,它会调用 previewingContext:viewControllerForLocation 代理方法,传递一个符合 UIViewControllerPreviewing 协议的 previewingContext 对象。使用此方法配置并返回一个视图控制器以进行预览。
- (nullable UIViewController *)previewingContext:(nonnull id)previewingContext viewControllerForLocation:(CGPoint)location {
    NSLog(@"%@", NSStringFromCGPoint(location));
    
    UITableViewCell *cell = (UITableViewCell *)previewingContext.sourceView;
    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
    
    XWTestViewController *vc = [[XWTestViewController alloc] init];
    vc.index = (indexPath.row % 2 == 0 ? 0 : 1);
    
    // 调整不被虚化的范围,按压的那个cell不被虚化(轻轻按压时周边会被虚化,再少用力展示预览,再加力跳页至设定界面)
//    CGRect rect = CGRectMake(0, 0, self.view.frame.size.width,200);
//    previewingContext.sourceRect = rect;
    return vc;
}
  • 当系统察觉到足够的压力可以触发 3D TouchPopViewController 时,会调用 previewingContext:commitViewController: 代理方法:
- (void)previewingContext:(id)previewingContext commitViewController:(UIViewController *)viewControllerToCommit {
    NSLog(@"%s", __func__);
    
    [self showViewController:viewControllerToCommit sender:self];
//    [self.navigationController pushViewController:viewControllerToCommit animated:YES];
}

3.2.2.3、 添加Peek状态下,上划时的快速操作
如果我们想在预览的时候,想做一些操作,那我们可以在 Peek 状态下,添加一些 Action 或者 Group,以提供快速操作。要添加快速操作,需要在预览控制器中重写 - (NSArray> *)previewActionItems 方法,示例代码如下:

- (NSArray> *)previewActionItems {
    UIPreviewAction *action1 = [UIPreviewAction actionWithTitle:@"收藏" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
        NSLog(@"收藏");
    }];
    
    UIPreviewAction *action2 = [UIPreviewAction actionWithTitle:@"分享" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
        NSLog(@"分享");
    }];
    
    NSArray *actions = @[action1,action2];
    
    return actions;
}

至此,3D Touch 关于 主屏交互预览和跳转(Peek and Pop) 的内容就到此结束,LivePhoto的内容开发中很少用到,所以就略去了,后续有时间的话,会专门写一篇关于 LivePhoto 的文章补上。

四、Author

如果你有什么建议,可以关注我,直接留言,留言必回。

五、参考

iOS-3DTouch学习二:Peek & Pop
iOS开发之3D Touch详解

你可能感兴趣的:(iOS之3D Touch)