美文网首页动画
3D Touch 简单应用

3D Touch 简单应用

作者: 那时天很蓝 | 来源:发表于2015-11-20 23:11 被阅读867次

    今年苹果发布了 iPhone 6s 和 iPhone 6s Plus,其中最引人关注的更新便是加入了全新的触控方式——3D Touch。最近几天简单地研究了一下,跟大家分享一下我的一些经验。

    UIApplicationShortcutItems


    当你重按应用的图标时,会弹出类似这样的小菜单(以微信为例):


    ShortcutItems

    添加这样的快捷菜单主要有 静态动态 两种方法:

    静态方法

    参看 UIApplicationShortcutItems-苹果官方文档

    ShortcutItems 变量说明

    可以看到 UIApplicationShortcutItemTitleUIApplicationShortcutItemType 这两个变量是必须的。

    我们在项目的 info.plist 文件中添加如下信息:

    静态添加 `ShortcutItems`

    运行结果:


    动态方法

    动态方法是在项目中添加代码:

    UIApplicationShortcutItem *item1 = [[UIApplicationShortcutItem alloc] initWithType:@"one" localizedTitle:@"Title One" localizedSubtitle:@"Sub one" icon:[UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypePlay] userInfo:nil];
    UIApplicationShortcutItem *item2 = [[UIApplicationShortcutItem alloc] initWithType:@"two" localizedTitle:@"Title Two" localizedSubtitle:@"Sub two" icon:[UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeHome] userInfo:nil];
    
    [UIApplication sharedApplication].shortcutItems = @[item1, item2];
    

    当该段代码在程序中被执行过一次后才会被添加到主屏幕的 ShortcutItems 菜单中。
    运行结果:

    注意: ShortcutItems 会优先加载静态方法添加的,然后加载动态方法添加的,并且同时只能拥有最多4个ShortcutItems。

    选择 ShortcutItem 后的回调

    AppDelegate 根据 shortcutItem.type 判断回调方法:

    - (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
    
        if ([shortcutItem.type isEqualToString:@"one"]) {
            NSLog(@"Choose One");
            MyViewController *vc = [[MyViewController alloc] init];
            vc.title = @"One";
            vc.view.backgroundColor = [UIColor whiteColor];
            [self.window.rootViewController showViewController:vc sender:nil];
        } else if ([shortcutItem.type isEqualToString:@"two"]) {
            NSLog(@"Choose Two");
            MyViewController *vc = [[MyViewController alloc] init];
            vc.title = @"Two";
            vc.view.backgroundColor = [UIColor orangeColor];
            [self.window.rootViewController showViewController:vc sender:nil];
        }    
    }
    

    按压力度感应


    在9.0后 UITouch 新增这样两个属性:

    9.0新增touch属性

    我们创建一个继承于 UIView 的自定义View,这里我们首先要判断一下设备是否支持3D Touch:

    - (BOOL)check3DTouch {
        if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
            return YES;
        } else {
            return NO;
        }
    }
    

    然后在 touchesMoved 中调用方法:

    - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        
        if ([self check3DTouch]) {
            UITouch *touch = [touches anyObject];
            self.backgroundColor = [UIColor colorWithRed:touch.force / touch.maximumPossibleForce  green:0.0f blue:0.0f alpha:1.0f];
        } else {
            NSLog(@"CAN NOT USE 3D TOUCH!");
        }
    }
    

    这样我们我创建了一个可以根据按压力度改变颜色的View。

    Peek & Pop


    Peek和Pop:

    Peek是指重按一下后出现的预览,Pop是在Peek后进一步按压后进入预览的视图控制器。
    首先遵循代理 <UIViewControllerPreviewingDelegate>
    然后监测设备是否支持3D Touch,若支持则对需要响应Peek操作的视图进行注册:

    - (void)check3DTouch {
        if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
            [self registerForPreviewingWithDelegate:self sourceView:_label];
        }
    }
    

    Peek的代理方法

    - (UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location {
          MyViewController *vc = [[MyViewController alloc] init];
          vc.title = @"Hello";
          vc.view.backgroundColor = [UIColor cyanColor];
          return vc;
    }
    

    其实就是返回一个视图控制器实例,但是看网上说这个方法会被多次调用,我实际测试有时会调用多次,有时只调用一次,还是不太清楚具体调用情况,有知道的朋友欢迎交流一下。为了保险起见,还是建议写成下面的形式:

    - (UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location {
        if ([self.presentedViewController isKindOfClass:[MyViewController class]]) {
            return nil;
        } else {
            MyViewController *vc = [[MyViewController alloc] init];
            vc.title = @"Hello";
            vc.view.backgroundColor = [UIColor cyanColor];
            return vc;
        }
    }
    

    至于Pop的方法就更简单了,直接调用下面的方法:
    Pop的代理方法

    - (void)previewingContext:(id<UIViewControllerPreviewing>)previewingContext commitViewController:(UIViewController *)viewControllerToCommit {
        [self showViewController:viewControllerToCommit sender:self];
    }
    

    PreviewAction Items

    有时在进入Peek但未Pop的时候,我们可以向上滑动选 PreviewAction Items


    PreviewAction Items

    PreviewAction Items 是在被预览的viewController下面添加下面方法实现的:

    - (NSArray<id<UIPreviewActionItem>> *)previewActionItems {
        UIPreviewAction *action1 = [UIPreviewAction actionWithTitle:@"Default" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
            
        }];
        UIPreviewAction *action2 = [UIPreviewAction actionWithTitle:@"Selected" style:UIPreviewActionStyleSelected handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
            
        }];
        UIPreviewAction *action3 = [UIPreviewAction actionWithTitle:@"Destructive" style:UIPreviewActionStyleDestructive handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
            
        }];
        NSArray *actions = @[action1, action2, action3];
        
        UIPreviewActionGroup *group = [UIPreviewActionGroup actionGroupWithTitle:@"Actions Group" style:UIPreviewActionStyleDefault actions:actions];
        
        UIPreviewAction *action4 = [UIPreviewAction actionWithTitle:@"Single Action" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
            
        }];
    
        NSArray *array = @[group, action4];
    
        return array;
    }
    

    通过返回 UIPreviewActionUIPreviewActionGroup 组成的数组实现。

    一个viewController下注册多个视图控件

    前面说到如果要让视图控件响应Peek操作需要对其进行注册,但是如果一个viewController中有多个控件需要响应Peek并且可能不知道何时会出现的时候(譬如添加了一个tableView后,需要每一个单独的cell独立响应一个Peek操作),是不可能一个一个注册的,这个时候我们可以直接将- (void)check3DTouch 中的代码改成:

    - (void)check3DTouch {
        if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
            [self registerForPreviewingWithDelegate:self sourceView:self.view];
        }
    }
    

    我们直接注册整个view,根据peek代理方法中的属性location 判断响应的UI控件

    - (UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location {
    
        if (CGRectContainsPoint(_tableView.frame, location)) {
            if ([self.presentedViewController isKindOfClass:[DisplayViewController class]]) {
                return nil;
            } else {
                location = [self.view convertPoint:location toView:_tableView];
                NSIndexPath *indexPath = [_tableView indexPathForRowAtPoint:location];
                NSLog(@"%@", indexPath);
                DisplayViewController *displayVC = [[DisplayViewController alloc] init];
                displayVC.title = [_tableView cellForRowAtIndexPath:indexPath].textLabel.text;
                displayVC.view.backgroundColor = [UIColor colorWithRed:arc4random() % 256 / 256.0
                                                                 green:arc4random() % 256 / 256.0
                                                                  blue:arc4random() % 256 / 256.0
                                                                 alpha:1.0];
                
                // peek预览窗口大小
                displayVC.preferredContentSize = CGSizeMake(0.0, 100 * indexPath.row);
                
                // 进入peek前不被虚化的rect
                previewingContext.sourceRect = [self.view convertRect:[_tableView cellForRowAtIndexPath:indexPath].frame fromView:_tableView];
                
                return displayVC;
            }
        }
        
        
        if ([self.presentedViewController isKindOfClass:[MyViewController class]]) {
            return nil;
        } else {
            if (CGRectContainsPoint(_label.frame, location)) {
                MyViewController *vc = [[MyViewController alloc] init];
                vc.title = @"Hello";
                vc.view.backgroundColor = [UIColor cyanColor];
                NSLog(@"New ViewController.");
                return vc;
            }
        }
        
        return nil;
    }
    

    3D Touch 小应用 —— 压力感应画板


    这段是引用了 crazypoo/TouchNewAPI 的代码

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        UITouch *touch = [touches anyObject];
        _touchPoint = [touch locationInView:_drawBoard];
    }
    
    - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        UITouch *touch = [touches anyObject];
        CGPoint currentPoint = [touch locationInView:_drawBoard];
        
        UIGraphicsBeginImageContext(_drawBoard.frame.size);
        [_drawBoard.image drawInRect:_drawBoard.frame];
        CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
        
        float lineWidth = 10.0f;
        if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
            lineWidth *= touch.force;
        }
        
        CGContextSetLineWidth(UIGraphicsGetCurrentContext(), lineWidth);
        CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0.0, 0.0, 0.0, 1.0);
        CGContextMoveToPoint(UIGraphicsGetCurrentContext(), _touchPoint.x, _touchPoint.y);
        CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
        CGContextStrokePath(UIGraphicsGetCurrentContext());
        _drawBoard.image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        
        _touchPoint = currentPoint;
    }
    

    以上是我的一点浅薄的心得,本文中所有代码都已经上传至 kisekied/3DTouchDemo 欢迎大家交流讨论。

    相关文章

      网友评论

      • 难却却:厉害了
      • L了个Y:一个viewController下注册多个视图控件下面的代码没看太懂啊?
        那时天很蓝:@文刀洋 注册整个view,转换当前3Dtouch的响应位置到tableview上,进行判断相应的视图是tableView上的哪一个视图(cell)
      • who_young:转换坐标解决了我的问题,大赞!
        那时天很蓝:@who_young 很高兴可以帮助到你:smile:
      • Se7ven:大神啊!挖藕

      本文标题:3D Touch 简单应用

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