你想知道的3D Touch开发全在这里了

前言

iPhone 6s和iPhone 6s Plus为多点触摸界面带来了强大的3D触摸新维度。这项新技术能够感知用户按下显示屏的深度,让他们比以往任什么时候候都更能使用你的应用程序和游戏。更多关于3D Touch的介绍能够参见这里html

正文

接下来会介绍一下全部关于3D Touch开发的一些内容。ios

0.判断3D Touch是否可用

先判断设备是否支持3D Touch,这里主要用到的类是:UITraitCollection。在iOS9以后,可使用该类判断设备是否支持3D Touch,苹果官方说明以下:git

3D Touch and Trait Collections

Starting in iOS 9, you can use this class to check whether the device on which your app is running supports 3D Touch. Read the value of the forceTouchCapability property on the trait collection for any object in your app with a trait environment. For information about trait environments, see UITraitEnvironment. For the possible values of the force touch capability property, see the UIForceTouchCapability enumeration.github

Because a user can turn off 3D Touch in Settings, check the value of the forceTouchCapability property in your implementation of the traitCollectionDidChange:method, and adjust your code paths according to the property’s value.web

主要是使用了forceTouchCapability属性,该属性的枚举值包括:macos

//未知
UIForceTouchCapabilityUnknown = 0,
//不可用
UIForceTouchCapabilityUnavailable = 1,
//可用
UIForceTouchCapabilityAvailable = 2

用户在使用APP的时候也有可能在设置中关闭3D Touch,这个时候能够实现traitCollectionDidChange:代理方法去监听是否改变:(在VC中实现UITraitEnvironment协议)api

#pragma mark - UITraitEnvironment
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
    if (previousTraitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
        [self showAlertWithStrig:@"3D Touch已关闭"];
    }else if (previousTraitCollection.forceTouchCapability == UIForceTouchCapabilityUnavailable) {
        [self showAlertWithStrig:@"3D Touch已打开"];
    }
}

这里注意:拿到的traitcollection是previousTraitCollection浏览器

1.Home screen quick action API(主屏幕交互)

该API主要用于添加应用程序图片的快捷方式,以预测并加速用户与应用的互动。ruby

1.1.用例

Demo

1.2.代码实例

两种方法实现该特性,直接使用代码开发,或者直接在Info.plist文件配置。微信

1.2.1.Static quick actions

直接在application:didFinishLaunchingWithOptions:方法中处理:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
     
    NSMutableArray *shortCutItemArr = [NSMutableArray arrayWithCapacity:4];
    UIApplicationShortcutIcon *icon1 = [UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeSearch];
    UIApplicationShortcutItem *shortItem1 = [[UIApplicationShortcutItem alloc] initWithType:@"com.zhanggui.Demo.search" localizedTitle:@"搜索" localizedSubtitle:@"搜索想要的电影" icon:icon1 userInfo:nil];
    [shortCutItemArr addObject:shortItem1];
    [UIApplication sharedApplication].shortcutItems = shortCutItemArr;
    return YES;
}

设置shortcutItems便可。

1.2.2.Dynamic quick actions

直接使用Info.plist文件配置:

<array>
    <dict>
        <key>UIApplicationShortcutItemIconType</key>
        <string>UIApplicationShortcutIconTypeShare</string>
        <key>UIApplicationShortcutItemTitle</key>
        <string>取票码</string>
        <key>UIApplicationShortcutItemType</key>
        <string>com.zhanggui.Demo.getTicket</string>
        <key>UIApplicationShortcutItemUserInfo</key>
        <dict>
            <key>key2</key>
            <string>value2</string>
        </dict>
    </dict>
</array>

关于key值的介绍,能够参见Info.plist Keys and Values
处理点击元素监听:

- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
    NSLog(@"%@",shortcutItem);
}

在AppDelegate中实现协议application:performActionForShortcutItem:completionHandler:方法便可。

1.3.核心类说明

UIApplicationShortcutItem:3D Touch弹框中每一条操做元素。
UIApplicationShortcutIcon:操做元素的icon。

1.3.1.UIApplicationShortcutItem
#if USE_UIKIT_PUBLIC_HEADERS || !__has_include(<UIKitCore/UIApplicationShortcutItem.h>)
//
//  UIApplicationShortcutItem.h
//  UIKit
//
//  Copyright © 2015-2018 Apple Inc. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKitDefines.h>

NS_ASSUME_NONNULL_BEGIN

@class UIImage;

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);

UIKIT_EXTERN API_AVAILABLE(ios(9.0)) API_UNAVAILABLE(tvos) API_UNAVAILABLE(macos)
@interface UIApplicationShortcutIcon : NSObject <NSCopying>

// Create an icon using a system-defined image.
+ (instancetype)iconWithType:(UIApplicationShortcutIconType)type;

// Create an icon from a custom image.
// The provided image named will be loaded from the app's bundle
// and will be masked to conform to the system-defined icon style.
+ (instancetype)iconWithTemplateImageName:(NSString *)templateImageName;

@end

UIKIT_EXTERN API_AVAILABLE(ios(9.0)) API_UNAVAILABLE(tvos) API_UNAVAILABLE(macos)
@interface UIApplicationShortcutItem : NSObject <NSCopying, NSMutableCopying>

- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithType:(NSString *)type localizedTitle:(NSString *)localizedTitle localizedSubtitle:(nullable NSString *)localizedSubtitle icon:(nullable UIApplicationShortcutIcon *)icon userInfo:(nullable NSDictionary<NSString *, id <NSSecureCoding>> *)userInfo NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithType:(NSString *)type localizedTitle:(NSString *)localizedTitle;

// An application-specific string that identifies the type of action to perform.
@property (nonatomic, copy, readonly) NSString *type;

// Properties controlling how the item should be displayed on the home screen.
@property (nonatomic, copy, readonly) NSString *localizedTitle;
@property (nullable, nonatomic, copy, readonly) NSString *localizedSubtitle;
@property (nullable, nonatomic, copy, readonly) UIApplicationShortcutIcon *icon;

// Application-specific information needed to perform the action.
// Will throw an exception if the NSDictionary is not plist-encodable.
@property (nullable, nonatomic, copy, readonly) NSDictionary<NSString *, id <NSSecureCoding>> *userInfo;

@end

UIKIT_EXTERN API_AVAILABLE(ios(9.0)) API_UNAVAILABLE(tvos) API_UNAVAILABLE(macos)
@interface UIMutableApplicationShortcutItem : UIApplicationShortcutItem

// An application-specific string that identifies the type of action to perform.
@property (nonatomic, copy) NSString *type;

// Properties controlling how the item should be displayed on the home screen.
@property (nonatomic, copy) NSString *localizedTitle;
@property (nullable, nonatomic, copy) NSString *localizedSubtitle;
@property (nullable, nonatomic, copy) UIApplicationShortcutIcon *icon;

// Application-specific information needed to perform the action.
// Will throw an exception if the NSDictionary is not plist-encodable.
@property (nullable, nonatomic, copy) NSDictionary<NSString *, id <NSSecureCoding>> *userInfo;

@end

NS_ASSUME_NONNULL_END

#else
#import <UIKitCore/UIApplicationShortcutItem.h>
#endif

经常使用方法:
建立一个UIApplicationShortcutItem:

- (instancetype)initWithType:(NSString *)type localizedTitle:(NSString *)localizedTitle localizedSubtitle:(nullable NSString *)localizedSubtitle icon:(nullable UIApplicationShortcutIcon *)icon userInfo:(nullable NSDictionary<NSString *, id <NSSecureCoding>> *)userInfo NS_DESIGNATED_INITIALIZER;
 
- (instancetype)initWithType:(NSString *)type localizedTitle:(NSString *)localizedTitle;

其中:

  • type表明:必传,app定义的主屏幕快速操做类型,能够用于肯定某元素点击
  • localizedTitle:展现的title
  • localizedSubtitle:sub title
  • icon:该item左侧或者右侧的icon,类型为UIApplicationShortcutIcon
  • userInfo:用户自定义的一些信息
1.3.2.UIApplicationShortcutIcon

icon有两种获取方式:一种是使用系统自带的类型,一种是用户自定义(根据图片名)。

1.3.2.1 系统自带类型
// Create an icon using a system-defined image.
+ (instancetype)iconWithType:(UIApplicationShortcutIconType)type;

系统自带类型包括如下几种:

系统自带icon类型

1.3.2.1 用户自定义
// Create an icon from a custom image.
// The provided image named will be loaded from the app's bundle
// and will be masked to conform to the system-defined icon style.
+ (instancetype)iconWithTemplateImageName:(NSString *)templateImageName;

用户自定义的图片格式规定以下:

square, single color, and 35x35 points(正方形、单色、35*35大小)

若是图片找不到,则展现为原点。效果可本身测试一下。

1.4.使用方式

  1. 直接使用Info.plist文件进行配置
  2. 直接编写代码

当同时使用二者时,会进行叠加,而且Info.plist配置中的元素会优先于AppDelegate中配置的元素展现。

1.5.个数限制

常规状况下,最多能够自定义4个Home screen quick action。在APP未上线以前,能够看到自定义的4个,多余的将会被忽略。在APP上线以后,将会有一个系统自带的item:分享 “APP名称”。总共不超过5个。

1.6.使用场景

可以进行快捷操做的事件,好比微信的扫一扫,微信支付、个人电影票等。

2.UIKit peek and pop API(预览和跳转)

UIKit的peek和pop API容许开发者在维护用户上下文的同时,在应用中提供对附加内容的轻松访问。使用peek quick actions能够为应用的触摸和按住操做提供按下的替换。
peek:当用户点击特定的view,会提供一个额外的预览视图。
pop:确认查看该内容,而且导航到该内容详情。
在iOS9以及之后的SDK中,为UIViewController提供了注册3D Touch和取消注册3D Touch的新方法,它们是:

// Registers a view controller to participate with 3D Touch preview (peek) and commit (pop).
- (id <UIViewControllerPreviewing>)registerForPreviewingWithDelegate:(id<UIViewControllerPreviewingDelegate>)delegate sourceView:(UIView *)sourceView NS_AVAILABLE_IOS(9_0);
- (void)unregisterForPreviewingWithContext:(id <UIViewControllerPreviewing>)previewing NS_AVAILABLE_IOS(9_0);

在注册方法中,sourceView就是要添加3D Touch的view。调用该注册方法的时候,它作了三件事:

  • 注册调用该方法的控制器参与3D Touch的预览(preview)和执行(commit)
  • 从接收方的视图层级结构中,将sourceView指定为响应强按压的视图
  • 指定委托对象,当用户进行强按压时依次请求peek和pop,该委托对象用于协调操做(vc实现了peek和pop代理方法)

You can designate more than one source view for a single registered view controller, but you cannot designate a single view as a source view more than once.

你能够在一个vc中指定多个sourceView,可是不能同一个view指定为sourceView屡次。
注册方法返回的上下文对象的生命周期由系统管理。若是你须要指明取消注册该vc,把该context对象传给unregisterForPreviewingWithContext:。若是开发者不取消注册,系统会在改VC释放的时候自动取消注册。
Demo中的注册方法实现以下:

if ([self.imageView respondsToSelector:@selector(traitCollection)]) {
    if([self.traitCollection respondsToSelector:@selector(forceTouchCapability)]) {
        if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
            [self registerForPreviewingWithDelegate:(id)self sourceView:self.imageView];
        }
    }
}

注册完成以后,须要实现UIViewControllerPreviewingDelegate,该代理主要有两个方法:

- (void)previewingContext:(id<UIViewControllerPreviewing>)previewingContext commitViewController:(UIViewController *)viewControllerToCommit {
    ImageViewController *imageVC = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"ImageVC"];
    [self presentViewController:imageVC animated:YES completion:nil];
}
- (UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location {
    ImageViewController *imageVC = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"ImageVC"];
    imageVC.preferredContentSize = CGSizeMake(0.0, 300);
    return imageVC;
}

第一个方法用于处理commit操做,也就是确认操做,确认3D Touch执行操做。
第二个方法在用户按下源视图显示peek时调用,实现此方法用以返回预览视图控制器。(这里返回的是ImageViewController)。若是此处返回nil,将会禁用预览。此处还会使用到previewingContext.sourceReact,该属性主要是在sourceView坐标系中,矩形响应用户的3D触摸,当矩形周会内容模糊时,矩形在视觉上保持清晰,具体可参见视频: https://github.com/ScottZg/MarkDownResource/blob/feature/addrubyimagefile/3DTouch/sourcReact.MP4

2.1.快速操做

可使用该API进行快速操做需求开发,例如微信重压聊天列表中的某个cell,就能够弹出快速操做:再也不关注、删除。这些快速操做很简单:在预览的VC中添加下面的代码便可。实例代码以下:

- (NSArray<id<UIPreviewActionItem>> *)previewActionItems {
    NSMutableArray *arrItem = [NSMutableArray arrayWithCapacity:2];
    UIPreviewAction *cancel = [UIPreviewAction actionWithTitle:@"取消" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
        NSLog(@"cancel");
    }];
    UIPreviewAction *ok = [UIPreviewAction actionWithTitle:@"删除" style:UIPreviewActionStyleDestructive handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
        self.imageView.image = nil;
    }];
    
    UIPreviewAction *select = [UIPreviewAction actionWithTitle:@"选中" style:UIPreviewActionStyleSelected handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
        NSLog(@"selected");
    }];
//    组操做
    UIPreviewAction *add = [UIPreviewAction actionWithTitle:@"增长" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
        NSLog(@"增长");
    }];
    UIPreviewAction *update = [UIPreviewAction actionWithTitle:@"更新" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
        NSLog(@"更新");
    }];
    UIPreviewActionGroup *group = [UIPreviewActionGroup actionGroupWithTitle:@"更多操做" style:UIPreviewActionStyleDefault actions:@[add,update]];
    [arrItem addObject:cancel];
    [arrItem addObject:ok];
    [arrItem addObject:select];
    [arrItem addObject:group];
    return arrItem;
}

这里主要实现了previewActionItems方法,用来设置预览的Action元素,这里涉及到的类有:

  • UIPreviewAction:预览的Action,当用户在支持3D Touch而且在peek视图下向上滑动,便可看到这些Action。
  • UIPreviewActionItem:协议,包含了一个只读的title。
  • UIPreviewActionGroup:操做组,能够设置一个操做组进行操做,例如二次确认。
2.1.1.UIPreviewAction

具体的操做元素,包括title、类型以及事件处理。其中style有如下三种类型:

typedef NS_ENUM(NSInteger,UIPreviewActionStyle) {
    UIPreviewActionStyleDefault=0,  //默认
    UIPreviewActionStyleSelected,  //选中
    UIPreviewActionStyleDestructive,  //销毁:红色
} NS_ENUM_AVAILABLE_IOS(9_0);
2.1.2.UIPreviewActionItem

协议,包含了一个属性title。表明操做action的title。

2.1.3.UIPreviewActionGroup

操做组,支持一个元素多个操做,具体能够参见下面的小视频:https://github.com/ScottZg/MarkDownResource/blob/feature/addrubyimagefile/3DTouch/group.MP4

3.Web view peek and pop API

在网页中,对于网页中的连接,peek和pop是默认支持的,能够经过设置allowsLinkPreview进行开启或关闭:
WKWebView:

/*! @abstract A Boolean value indicating whether link preview is allowed for any
 links inside this WKWebView.
 @discussion The default value is YES on Mac and iOS.
 */
@property (nonatomic) BOOL allowsLinkPreview API_AVAILABLE(macosx(10.11), ios(9.0));

UIWebView:

@property (nonatomic) BOOL allowsLinkPreview NS_AVAILABLE_IOS(9_0); // default is NO

默认状况下使用苹果自带的浏览器打开,这样就会跳出本身的应用。
尝试本身对wkwebview进行注册,而后本身设置peek和pop,发现无效。
可使用SFSafariViewController替代WebView。

SFSafariViewController使用presentViewController:animated:completion:方法展现。效果和push同样。

4.UITouch force properties

UITouch类提供了两个新的属性来支持自定义实现3D Touch:

// Force of the touch, where 1.0 represents the force of an average touch
@property(nonatomic,readonly) CGFloat force NS_AVAILABLE_IOS(9_0);
// Maximum possible force with this input mechanism
@property(nonatomic,readonly) CGFloat maximumPossibleForce NS_AVAILABLE_IOS(9_0);

具体使用能够参见Demo

参考文档

  1. Adopting 3D Touch on iPhone
  2. https://developer.apple.com/ios/3d-touch/
  3. registerForPreviewingWithDelegate
相关文章
相关标签/搜索