您的位置:首页 > 移动开发 > IOS开发

iOS 3D Touch

2016-09-11 13:15 190 查看
3D Touch 介绍
3D Touch的主要应用场景

3D Touch的主要功能模块
1Home Screen Quick Actions

2peek and pop

3Force Properties

Home Screen Quick Action使用与相关api详解
1静态标签

2动态标签

3响应标签的行为

Peek and Pop
Peek and Pop

Preview Action

Force Properties

附模拟器 3D Touch 测试方法

3D Touch 介绍

3D Touch 是 Apple 推出的通过压力感触区分轻按和重按来进行不同的用户交互,为 App 增加额外维度上的交互,它支持的系统版本为 i0S 9.0+,机型为 iPhone 6s。

3D Touch的主要应用场景

Apple 文档给出的应用介绍主要有两块:

1.A user can now press your Home screen icon to immediately access functionality provided by your app.

2.Within your app, a user can now press views to see previews of additional content and gain accelerated access to features.

  第一部分的应用是我们可以通过3D手势,在主屏幕上的应用Icon处,直接进入应用的响应功能模块。这个功能就例如我们上面的日历示例,会在Icon旁边出现一个菜单,点击菜单我们可以进入相应的功能单元。

  我个人理解,这个功能,push消息功能加上iOS8推出的扩展today功能,这三个机制使iOS应用变得无比灵活方便,用户可以不需付出寻找的时间成本来快速使用自己需要的功能。

  第二部分是对app的一个优化,用户可以通过3D Touch手势在view上来预览一些预加载信息,这样的设计可以使app更加简洁大方,交互性也更强。

  

  

3D Touch的主要功能模块

3D Touch 的功能主要分为以下三项:

1、Home Screen Quick Actions

  通过主屏幕的应用Icon,我们可以用3D Touch呼出一个菜单,进行快速定位应用功能模块相关功能的开发。如上面的日历。

2、peek and pop

  这个功能是一套全新的用户交互机制,在使用3D Touch时,ViewController中会有如下三个交互阶段:

 (1)提示用户这里有3D Touch的交互,会使交互控件周围模糊:

 

  


 (2)继续深按,会出现预览视图:

  


 (3)通过视图上的交互控件进行进一步交互:

  


这个模块的设计可以在网址连接上进行网页的预览交互。

3、Force Properties

  iOS 9 提供了一个新的交互参数:力度。可以检测某一交互的力度值,来做相应的交互处理。例如,我们可以通过力度来控制快进的快慢,音量增加的快慢等。

Home Screen Quick Action使用与相关api详解

iOS 9 提供了两种屏幕标签的设置方式,分别是静态标签和动态标签。

1、静态标签

  静态标签是我们在项目的配置plist文件中配置的标签,在用户安装程序后就可以使用,并且排序会在动态标签的前面。

  我们先来看静态标签的配置:

  首先,在info.plist文件中添加如下键值(我在测试的时候,系统并没有提示,只能手打上去):

  


  先添加了一个UIApplicationShortcutItems的数组,这个数组中添加的元素就是对应的静态标签,在每个标签中我们需要添加一些设置的键值:

必填项(下面两个键值是必须设置的):

UIApplicationShortcutItemType
- 这个键值设置一个快捷通道类型的字符串

UIApplicationShortcutItemTitle
- 这个键值设置标签的标题

选填项(下面这些键值不是必须设置的):

UIApplicationShortcutItemSubtitle
- 设置标签的副标题

UIApplicationShortcutItemIconType
- 设置标签 Icon 类型

UIApplicationShortcutItemIconFile
- 设置标签的 Icon 文件

UIApplicationShortcutItemUserInfo
- 设置信息字典(用于传值)

2、动态标签

动态标签是我们在程序中,通过代码添加的,与之相关的类,主要有三个:

UIApplicationShortcutItem
- 创建 3D Touch 标签的类

UIMutableApplicationShortcutItem
- 创建可变的 3D Touch 标签的类

UIApplicationShortcutIcon
- 创建标签中图片 Icon 的类

主要的类和 API

@interface UIApplicationShortcutItem : NSObject <NSCopying, NSMutableCopying>
//下面是两个初始化方法 通过设置type,title等属性来创建一个标签,这里的icon是UIApplicationShortcutIcon对象,我们后面再说
- (instancetype)initWithType:(NSString *)type localizedTitle:(NSString *)localizedTitle localizedSubtitle:(nullable NSString *)localizedSubtitle icon:(nullable UIApplicationShortcutIcon *)icon userInfo:(nullable NSDictionary *)userInfo NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithType:(NSString *)type localizedTitle:(NSString *)localizedTitle;
- //下面这是一些只读的属性,获取相应的属性值
@property (nonatomic, copy, readonly) NSString *type;
@property (nonatomic, copy, readonly) NSString *localizedTitle;
@property (nullable, nonatomic, copy, readonly) NSString *localizedSubtitle;
@property (nullable, nonatomic, copy, readonly) UIApplicationShortcutIcon *icon;
@property (nullable, nonatomic, copy, readonly) NSDictionary<NSString *, id <NSSecureCoding>> *userInfo;
@end


//这个类继承于 UIApplicationShortcutItem,创建的标签可变
@interface UIMutableApplicationShortcutItem : UIApplicationShortcutItem
@property (nonatomic, copy) NSString *type;
@property (nonatomic, copy) NSString *localizedTitle;@property (nullable, nonatomic, copy) NSString *localizedSubtitle;
@property (nullable, nonatomic, copy) UIApplicationShortcutIcon *icon;
@property (nullable, nonatomic, copy) NSDictionary<NSString *, id <NSSecureCoding>> *userInfo;
@end


//这个类创建标签中的 icon
@interface UIApplicationShortcutIcon : NSObject <NSCopying>
// 创建系统风格的icon
+ (instancetype)iconWithType:(UIApplicationShortcutIconType)type;
// 创建自定义的图片icon
+ (instancetype)iconWithTemplateImageName:(NSString *)templateImageName;
@end


系统风格 icon 的枚举:

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)
} NS_ENUM_AVAILABLE_IOS(9_0) __TVOS_PROHIBITED;


创建好标签后,需要将其添加如application的hortcutItems数组中

- (void)createItemsWithIcons {

UIApplicationShortcutIcon *icon1 = [UIApplicationShortcutIcon iconWithTemplateImageName:@"iCon1"];
UIApplicationShortcutIcon *icon2 = [UIApplicationShortcutIcon iconWithTemplateImageName:@"iCon2"];
UIApplicationShortcutIcon *icon3 = [UIApplicationShortcutIcon iconWithTemplateImageName:@"iCon3"];

// create several (dynamic) shortcut items
UIMutableApplicationShortcutItem *item1 = [[UIMutableApplicationShortcutItem alloc]initWithType:@"com.test.dynamic" localizedTitle:@"Dynamic Shortcut" localizedSubtitle:@"available after first launch" icon:icon1 userInfo:nil];
UIMutableApplicationShortcutItem *item2 = [[UIMutableApplicationShortcutItem alloc]initWithType:@"com.test.deep1" localizedTitle:@"Deep Link 1" localizedSubtitle:@"Launch Nav Controller" icon:icon2 userInfo:nil];
UIMutableApplicationShortcutItem *item3 = [[UIMutableApplicationShortcutItem alloc]initWithType:@"com.test.deep2" localizedTitle:@"Deep Link 2" localizedSubtitle:@"Launch 2nd Level" icon:icon3 userInfo:nil];

// add all items to an array
NSArray *items = @[item1, item2, item3];

// add this array to the potentially existing static UIApplicationShortcutItems
NSArray *existingItems = [UIApplication sharedApplication].shortcutItems;
NSArray *updatedItems = [existingItems arrayByAddingObjectsFromArray:items];
[UIApplication sharedApplication].shortcutItems = updatedItems;

}


3、响应标签的行为

  类似推送,当我们点击标签进入应用程序时,也可以进行一些操作,我们可以看到,在applocation中增加了这样一个方法:

- ( void )application:( UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:( void (^)( BOOLsucceeded))completionHandler NS_AVAILABLE_IOS ( 9 _0);


  当我们通过标签进入 App 时,就会在 AppDelegate中调用这样一个回调,我们可以获取shortcutItem的信息进行相关逻辑操作。

  这里有一点需要注意:我们在 App 的入口函数:

- ( BOOL )application:( UIApplication *)application didFinishLaunchingWithOptions:( NSDictionary *)launchOptions;


也需要进行一下判断,在launchOptions中有
UIApplicationLaunchOptionsShortcutItemKey
这样一个键,通过它,我们可以区别是否是从标签进入的 App,如果是则处理结束逻辑后,返回
NO
,防止处理逻辑被反复回调。

- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {

NSLog(@"A shortcut item was pressed. It was %@.", shortcutItem.localizedTitle);

if ([shortcutItem.type isEqualToString:@"com.test.deep1"]) {
[self launchViewController1];
}

if ([shortcutItem.type isEqualToString:@"com.test.deep2"]) {
[self launchViewController2];
}

}


注意:

1、快捷标签最多可以创建四个,包括静态的和动态的。

2、每个标签的题目和icon最多两行,多出的会用…省略

Peek and Pop

Peek and Pop

实现 Peek 和 Pop 也非常简单,需要以下步骤:

1、在需要预览的 ViewController 遵循并实现
UIViewControllerPreviewingDelegate
协议

2、调用
- (id <UIViewControllerPreviewing>)registerForPreviewingWithDelegate:(id<UIViewControllerPreviewingDelegate>)delegate sourceView:(UIView *)sourceView NS_AVAILABLE_IOS(9_0)
方法注册该 ViewController

3、在
- (nullable UIViewController *)previewingContext:(id <UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location NS_AVAILABLE_IOS(9_0)
代理方法中提供一个预览的 ViewController,并设置好 context 的 sourceRect.

4、在
- (void)previewingContext:(id <UIViewControllerPreviewing>)previewingContext commitViewController:(UIViewController *)viewControllerToCommit NS_AVAILABLE_IOS(9_0)
方法中调用
- (void)showDetailViewController:(UIViewController *)vc sender:(nullable id)sender NS_AVAILABLE_IOS(8_0)


# pragma mark - UIViewControllerPreviewingDelegate

- (UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location {

// check if we're not already displaying a preview controller
if ([self.presentedViewController isKindOfClass:[PreviewViewController class]]) {
return nil;
}

// shallow press: return the preview controller here (peek)
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UIViewController *previewController = [storyboard instantiateViewControllerWithIdentifier:@"PreviewView"];

return previewController;
}

- (void)previewingContext:(id<UIViewControllerPreviewing>)previewingContext commitViewController:(UIViewController *)viewControllerToCommit {

// deep press: bring up the commit view controller (pop)
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UIViewController *commitController = [storyboard instantiateViewControllerWithIdentifier:@"CommitView"];

[self showViewController:commitController sender:self];
}


注意:在开发 Peek and Pop 的过程中,请记住以下原则:

1、让合适的内容支持 Peek and Pop (不要滥用这项特性);

2、始终返回相同的预览界面 (保持一致性和可预测性);

3、不要在preview代理方法中花太多的时间;

4、为 context 设置正确的 sourceRect

Preview Action

  在 Preview 的过程中,用户可以上滑来唤出类似 Action Sheet 的菜单。实现这一功能只需要重写 ViewController 中的
- (NSArray <id <UIPreviewActionItem>> *)previewActionItems NS_AVAILABLE_IOS(9_0)
方法即可。 系统提供了和 UIAlertAction 非常类似的 UIPreviewAction,来实现 UIPreviewActionItem。

  与 Action Sheet 不同的是,系统提供了 UIPreviewActionGroup 类,实现子菜单的功能。

  其中,
UIPreviewAction
UIPreviewActionGroup
的 API 很简单,类似于
UIAlertAction


NS_CLASS_AVAILABLE_IOS(9_0) @protocol UIPreviewActionItem <NSObject>
/// UIPreviewAction 的标题
@property(nonatomic, copy, readonly) NSString *title;
@end

typedef NS_ENUM(NSInteger,UIPreviewActionStyle) {
UIPreviewActionStyleDefault=0,
UIPreviewActionStyleSelected,
UIPreviewActionStyleDestructive,
} NS_ENUM_AVAILABLE_IOS(9_0);

NS_CLASS_AVAILABLE_IOS(9_0) @interface UIPreviewAction : NSObject <NSCopying,UIPreviewActionItem>
/// UIPreviewAction 的点击事件
@property(nonatomic, copy, readonly) void (^handler)(id<UIPreviewActionItem> action, UIViewController *previewViewController);

+ (instancetype)actionWithTitle:(NSString *)title style:(UIPreviewActionStyle)style handler:(void (^)(UIPreviewAction *action, UIViewController *previewViewController))handler;

@end

NS_CLASS_AVAILABLE_IOS(9_0) @interface UIPreviewActionGroup : NSObject <NSCopying,UIPreviewActionItem>
+ (instancetype)actionGroupWithTitle:(NSString *)title style:(UIPreviewActionStyle)style actions:(NSArray<UIPreviewAction *> *)actions;
@end

NS_ASSUME_NONNULL_END


Force Properties

iOS 9.0 之后,
UITouch
类中增加的一些新属性:

@property(nonatomic,readonly) UITouchType type


触摸的类型。

typedef NS_ENUM(NSInteger, UITouchType) {
// 一个手指(在屏幕上)直接接触
UITouchTypeDirect,
// 间接接触
UITouchTypeIndirect,
// 触控笔接触
UITouchTypeStylus NS_AVAILABLE_IOS(9_1),
} NS_ENUM_AVAILABLE_IOS(9_0);


@property(nonatomic,readonly) CGFloat force


触摸的压力值,其中 1.0 表示一个平均压力值(有系统预定,而不是用户自己设定)。

@property(nonatomic,readonly) CGFloat altitudeAngle


触控笔的高度(弧度)。

@property(nonatomic,readonly) CGFloat maximumPossibleForce


触摸的最大可能的压力值。

@property(nonatomic,readonly) UITouchProperties estimatedProperties


当前触摸对象估计的触摸特性。

typedef NS_OPTIONS(NSInteger, UITouchProperties) {
// 压力除魔属性
UITouchPropertyForce = (1UL << 0),
// 方位角除魔属性
UITouchPropertyAzimuth = (1UL << 1),
// 海拔高度除魔属性
UITouchPropertyAltitude = (1UL << 2),
// 位置除魔属性
UITouchPropertyLocation = (1UL << 3), // For predicted Touches
} NS_AVAILABLE_IOS(9_1);


@property(nonatomic, readonly) CGFloat altitudeAngle


高度值,仅适用于触控笔触摸类型。当笔平行于平面时,该值为0;当笔垂直于平面时,该值为Pi / 2 。

@property(nonatomic,readonly) NSNumber * _Nullable estimationUpdateIndex


一个可以让你关联更新后的触摸和原始触摸的索引值,当每个触摸对象的触摸特性发生变化时,该值将会单独增加。

@property(nonatomic,readonly) UITouchProperties estimatedPropertiesExpectingUpdates
- 一组触摸属性,希望在未来有传入的更新。如果没有预计属性的更新,当前值是最终的估计值。

iOS 9.0 之后,
UITouch
类中增添的一些新方法:

- (CGPoint)preciseLocationInView:(UIView *)view;


当可用时,返回一个精确的触摸位置。

- (CGPoint)precisePreviousLocationInView:(UIView *)view;


当可用时,返回一个精确的前一个触摸位置。

- (CGFloat)azimuthAngleInView:(nullable UIView *)view


返回愁莫比的方位角(弧度)。

- (CGVector)azimuthUnitVectorInView:(UIView *)view;


返回触控笔的镇的方向上的而单位向量。

获取触摸压力值很简单:

-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSArray *arrayTouch = [touches allObjects];
UITouch *touch = (UITouch *)[arrayTouch lastObject];
NSLog(@"%f",touch.force);
}


附:模拟器 3D Touch 测试方法

  在模拟器上是不能使用 3D Touch 功能的,但是难不倒万能的「程序员」。在 GitHub 上出现了一个可以在模拟器上实现 3D Touch 功能的插件 .SBShortcutMenuSimulator,具体使用方法还是移步 GitHub 自行查阅。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ios