美文网首页iOS开发实用工具iOS开发
3D Touch实践讲解 --解决开发中的困惑点

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

作者: 李周 | 来源:发表于2017-07-20 16:44 被阅读268次

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的实现并不难,但是有一些细节和思路还是要好好优化,等将官方的相关文档全部看了一遍之后,再好好总结一次。

相关文章

网友评论

本文标题:3D Touch实践讲解 --解决开发中的困惑点

本文链接:https://www.haomeiwen.com/subject/svticxtx.html