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<NSString *, id <NSSecureCoding>> *)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 <UIViewControllerPreviewing>)registerForPreviewingWithDelegate:(id<UIViewControllerPreviewingDelegate>)delegate sourceView:(UIView *)sourceView;
  • 遵循协议
@interface ViewController () <UIViewControllerPreviewingDelegate>
@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<UIViewControllerPreviewing>)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<UIViewControllerPreviewing>)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<id<UIPreviewActionItem>> *)previewActionItems 方法,示例代码如下:

- (NSArray<id<UIPreviewActionItem>> *)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详解

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