3D Touch实践讲解 --解决开发中的困惑点

3D Touch是我一直想学习的功能,晚上无意间在手机上按压了不同的应用(这么一说暴露了自己用的iPhone 6以上的设备),发现常用的应用都加了该功能。为了自己的好奇心,跟着官方文档做了一遍,把过程中的思路和易错点记录下来。

目前3D Touch功能只能在iPhone6并且支持3D Touch的设备中使用

1 主屏幕Icon的3D Touch效果

对于能开启3D Touch 的设备而言,在主屏幕中以一定的力度按压应用的图标,会弹出一个预览的功能框,功能框中包括一些功能的快捷按钮,点击之后会启动应用并快捷的开启应用相关功能。具体的实现的步骤如下:

主屏幕Icon按压效果
步骤一 UIApplicationShortcutItem 添加快捷按钮

每一个快捷按钮都是一个UIApplicationShortcutItem,可以通过两种方法设置:

① 在应用的Info.plist文件中设置静态快捷按钮
Info.plist文件中添加快捷按钮

这种方法很简单,在UIApplicationShortcutItems键中添加快捷键的信息。UIApplicationShortcutItem属性包括:
UIApplicationShortcutItemType(必选):快捷键的唯一标识符,用来识别以便于实现相对应的功能
UIApplicationShortcutItemTitle(必选):标题,黑色字体
UIApplicationShortcutItemIconType(可选):显示的图标,图标可以是系统或者自定义
UIApplicationShortcutItemUserInfo(可选):用户自定义信息
UIApplicationShortcutItemSubtitle(可选):副标题,灰色字体

② 在应用中设置动态快捷按钮
UIApplicationShortcutIcon *icon = [UIApplicationShortcutIcon iconWithTemplateImageName:@"pay-select.png"];
UIApplicationShortcutItem *home = [[UIApplicationShortcutItem alloc] initWithType:@"lizhou.home" localizedTitle:@"热门单品" localizedSubtitle:@"热卖的物品任你挑" icon:icon userInfo:nil];
[UIApplication sharedApplication].shortcutItems = @[home];

重点

1.设置Item的title和subtitle

title和subtitle分别只占用一行,多余的文本系统会默认添加省略号。


系统默认添加省略号效果图
2.设置icon

快捷键的图标可选择自定义图标或系统提供的图标。自定义的图片大小应为35x35,而且因为系统会统一模糊化图片,所以提供的图标尽量为单色,不然只有可能只显示黑色的正方形而不使用提供的图标。

@interface UIApplicationShortcutIcon : NSObject <NSCopying>
// 使用系统提供的图片
+ (instancetype)iconWithType:(UIApplicationShortcutIconType)type;
// 创建自定义的图片,将会使图片模糊成系统定义icon的风格
+ (instancetype)iconWithTemplateImageName:(NSString *)templateImageName;
@end

自定义图标在Info.plist中可以直接填图片的名称。

3. UIApplicationShortcutItemUserInfo 用户自定义信息

之前一直不能理解静态和动态设置快捷键之间有什么差别,看了几个应用如微信、微博、支付宝等都是普通的快捷键,直到看到快看漫画,该应用的"继续阅读"项中添加了动态的效果,才意识到两者之间最大的差异性就是UIApplicationShortcutItemUserInfo这个属性。

快看漫画动态快捷键效果图
4. 快捷键的数量
问题:在官方文档中明确的标记了应用最多只能设置4个自定义的item,为什么快看会有5个按钮?
回答:快看漫画中自定义的item就是四个,最后一个 "分享 ‘快看漫画’"项是苹果为每一个提交到应用商店的应用默认添加的,所以准确的说:

开发者自定义的item最多为4个,用户可视化的item最多为5个

步骤二 识别到用户对快捷键的点击

应用识别是由3D Touch的点击进行应用并执行相关的行为的方法分为两种情况:
① 应用处于运行中的状态

//app仍然在运行的时候,点击菜单项会触发以下的方法:
-(void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler
{
    if([shortcutItem.type hasSuffix:@"search"]){
        //  搜索更多
    } else if ([shortcutItem.type hasSuffix:@"share"]) {
        //分享
        NSString *title =shortcutItem.localizedTitle;
        
        NSDictionary *userInfo = shortcutItem.userInfo;
        
        UINavigationController *nav = (UINavigationController *)[_tabBarController selectedViewController];
        
        [nav pushViewController:[[TestViewController alloc] init] animated:YES];
    }
    completionHandler(YES);
}

② 应用被kill后,没有运行的情况下

//在app 被kill的时候,点击菜单项会触发以下的方法:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  _isEnterFrom3DTouch = true;
 //如果是通过3D Touch点击shortItem进入应用的话,那么UIApplicationLaunchOptionsShortcutItemKey一定返回相应的UIApplicationShortcutItem。
    UIApplicationShortcutItem *shortItem =[launchOptions objectForKey:UIApplicationLaunchOptionsShortcutItemKey];
    if (shortItem) {
//如果从3D Touch点击item进行,那么返回false,不再触发-(void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler方法
        _isEnterFrom3DTouch = false;
    }
  return _isEnterFrom3DTouch;
}

其实在设置item和收到item 的用户响应都不需要判断设备是否支持3D Touch,如果支持,按压才能识别。识别之后系统才会执行应用设置的3D Touch功能。

对于Icon设置的3D Touch以及相关的响应接收方法处理差不多了,下面是对应用内的3D Touch配置。

2 应用内的3D Touch效果

3D Touch效果图

上图中能清楚的看到整个过程包括两个阶段:
第一阶段 :模糊化周围,弹出一个类似弹框的界面 ----peek
第二阶段:加重按压,进入一个视图控制器 --- pop

实现整个效果的步骤如下:

步骤一 明确哪个控件有3D Touch功能并注册

像微博,一个cell中有两个3D Touch控件,而且两个控件的预览图也不同,并且点击在cell别的地方时不会显示3D Touch的效果。

微博添加3D Touch控件位置

所以需要对一个cell内部局部view进行3D Touch的注册,而不是像官方例子中对整个tableView进行注册:

if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
        [self registerForPreviewingWithDelegate:self sourceView:self.tableView];
    }

对于整体而言,首先我们注册的是具有3D Touch功能的控件 ---cell中的imageView(以微博为例),所以cell需要实现UIViewControllerPreviewingDelegate协议

@interface LZImageCell : UITableViewCell<UIViewControllerPreviewingDelegate>

并且实现UIViewControllerPreviewingDelegate协议的两个方法:

//显示 pop 的视图控制器
-(void)previewingContext:(id<UIViewControllerPreviewing>)previewingContext commitViewController:(UIViewController *)viewControllerToCommit
{
    if ([self.previewDelegate respondsToSelector:@selector(previewingContext:commit:)]) {
        [self.previewDelegate previewingContext:previewingContext commit:viewControllerToCommit];
    }
}
//显示peek 的视图控制器
-(UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location
{
    if ([self.previewDelegate respondsToSelector:@selector(previewingContext:viewControllerForLocation:)]) {    
        return [self.previewDelegate previewingContext:previewingContext location:location];
    }
    return nil;
}

因为cell本身是无法处理视图控制器之间的push,并且注册只能在ViewController类中注册,所以设置一个delegate,将数据传递到相应的viewController中进行处理:

-(UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext location:(CGPoint)location
{
//根据点击位获得当前点击cell的indexPath值
    NSIndexPath *indexpath = [self.tableView indexPathForRowAtPoint:location];
  //得到当前点击的cell的位置
    CGRect cellFrame = [self.tableView cellForRowAtIndexPath:indexpath].frame;
    //找到cell中响应3D Touch的图片控件位置和大小
    cellFrame.size.height = 200;
    cellFrame.origin.x = 10;
    cellFrame.size.width = [UIScreen mainScreen].bounds.size.width - 20;

    TestViewController *test = [[TestViewController alloc] init];
//这个值是设置显示test控制器中view的大小
    test.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width - 20, 200);
//周围都是模糊化,只有cellFrame值设置的部分为清晰的状态
    previewingContext.sourceRect = cellFrame;
    return test;
}
//pop 显示视图控制器
//commitViewController是在peek方法中设置的ViewController(如: TestViewController)
-(void )previewingContext:(id<UIViewControllerPreviewing>)previewingContext commit:(UIViewController *)commitViewController
{
    [self.navigationController pushViewController:commitViewController animated:YES];
}

将peek和pop设置完成之后,只差最后一步了 ---- 注册cell中的imageView

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    LZImageCell *cell = [tableView dequeueReusableCellWithIdentifier:imageCellStr forIndexPath:indexPath];
    
    cell.previewDelegate = self;
    
    if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
        //注册
        [self registerForPreviewingWithDelegate:cell sourceView:cell.backImageView];
    } else {
        NSLog(@"3D Touch 不可用");
    }
    return cell;
}

注意:如果你还按压imageView没有任何反应,说明你没有将图片的交互功能打开

backImageView.userInteractionEnabled = YES;

按压也是touch的一种,需要响应的,是不是傻!!

步骤二 对预览页中添加按钮

用户在peek页中向上滑动会出现设置的按钮,所以按钮设置在peek方法返回的ViewController中(如:TestViewController)

在peek页中添加按钮
-(NSArray<id<UIPreviewActionItem>> *)previewActionItems
{
    UIPreviewAction *likeAction = [UIPreviewAction actionWithTitle:@"喜欢" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
        
    }];
    
    UIPreviewAction *cancleAction = [UIPreviewAction actionWithTitle:@"保存" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
        
    }];

官方文档自己都说设置的UIPreviewAction按钮和UIAlertAction很类似,所以对于style的设置没什么不同。

如果按钮数过多,可以将类似的按钮放在同一个按钮组中


设置按钮组

点击之后才会显示相关的按钮。


就我个人认为实现3D Touch的实现并不难,但是有一些细节和思路还是要好好优化,等将官方的相关文档全部看了一遍之后,再好好总结一次。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,528评论 25 707
  • 独有特性 列表不同于元组和字符串的地方:可变 特殊函数 函数list可以把其他序列转换为列表 python 3.0...
    chf041阅读 311评论 0 0
  • 上午的随手拍 水果店的叔叔阿姨笑起来超暖 煎饼店的姐姐剪刀手超可爱
    长吉玉川阅读 219评论 0 3
  • 今天听音频,听到一句话,特别感同身受。 “你身边五个朋友能力的平均值,就是你的能力值。” 就像之前提到的一句话,你...
    一树绿叶阅读 464评论 2 2
  • 少年锦时丶楠海 你好,我是楠海。感谢你点开我的文章。写这样一期内容,就是想和关注我的朋友做一个交流。端一杯咖啡,享...
    行者楠海阅读 207评论 0 3