一、前言
3D Touch 是一種立體觸控技術(shù),被蘋果稱為新一代多點(diǎn)觸控技術(shù),是在Apple Watch 上采用的 Force Touch,屏幕可感應(yīng)不同的感壓力度觸控。 3D Touch,蘋果 iPhone 6s 的新功能,看起來類似 PC 上的右鍵。有 Peek 和 Pop 兩種新手勢。此功能的發(fā)布將手機(jī)屏幕的的操作坐標(biāo)由 xy 軸擴(kuò)大至 z 軸,增加了整整一個(gè)維度(即相對(duì)于多點(diǎn)觸摸在平面二維空間的操作,3D Touch 技術(shù)增加了對(duì)力度和手指面積的感知,可以通過長按快速預(yù)覽/查看你想要的短信/圖片/超鏈接等內(nèi)容,Peek 和 Pop 手勢的響應(yīng)時(shí)間可迅捷到 10ms 和 15ms),這在屏幕時(shí)代屬于非常偉大的創(chuàng)新。
demo地址
二、蘋果的3D Touch主要呈現(xiàn)方式
2.1、按功能劃分,有三種:
- 主屏交互(
Home Screen Interaction); - 預(yù)覽和跳轉(zhuǎn)(
Peek and Pop); -
LivePhoto;
注:本文主要介紹一下前兩種用法。
2.2、按3D Touch功能使用的位置劃分,有兩種:
-
手機(jī)桌面,效果圖如下:
微信
京東 -
應(yīng)用內(nèi),效果圖如下:
微信
三、使用
3.1、主屏交互(Home Screen Interaction);
所謂的主屏交互也就是在手機(jī)的桌面,用手指按壓應(yīng)用圖標(biāo),生成的幾個(gè)快捷操作按鈕的,效果圖如下:

主屏交互的按鈕有指定的模型類:
UIApplicationShortcutItem,添加 UIApplicationShortcutItem 有兩種方式:靜態(tài)添加和動(dòng)態(tài)添加。
靜態(tài)添加 和 動(dòng)態(tài)添加 的 區(qū)別:靜態(tài)設(shè)置是在應(yīng)用安裝的時(shí)候完成加載的,而動(dòng)態(tài)設(shè)置需要在運(yùn)行到對(duì)應(yīng)代碼時(shí)(runtime) 才加載,所以同時(shí)有靜態(tài)加載的 Item 和動(dòng)態(tài)加載的 Item時(shí),靜態(tài)加載的 Item 會(huì)排在前面。
3.1.1、靜態(tài)添加UIApplicationShortcutItem
靜態(tài)添加 UIApplicationShortcutItem 方式主要是在工程的 info.plist 文件中添加相關(guān)的屬性,如下圖:

// 數(shù)組中的元素就是上圖中的快捷選項(xiàng)標(biāo)簽
UIApplicationShortcutItems
// 標(biāo)簽標(biāo)題(必填)
UIApplicationShortcutItemTitle
// 標(biāo)簽的唯一標(biāo)識(shí) (必填)
UIApplicationShortcutItemType
// 使用系統(tǒng)圖標(biāo)的類型,如搜索、定位、home等(可選)
UIApplicationShortcutItemIconType
// 使用項(xiàng)目中的圖片作為標(biāo)簽圖標(biāo) (可選)
UIApplicationShortcutItemIconFile
// 標(biāo)簽副標(biāo)題 (可選)
UIApplicationShortcutItemSubtitle
// 字典信息,如傳值使用 (可選)
UIApplicationShortcutItemUserInfo
3.1.2、動(dòng)態(tài)添加UIApplicationShortcutItem
UIApplicationShortcutItem 可以看作是 3D Touch 點(diǎn)擊后,彈出菜單每行對(duì)應(yīng)的模型,一行對(duì)應(yīng)一個(gè) UIApplicationShortcutItem 對(duì)象。
動(dòng)態(tài)添加時(shí)就是我們可以通過生成 UIApplicationShortcutItem 對(duì)象數(shù)組,添加給 UIApplication 單例對(duì)象。
UIApplicationShortcutItem 初始方法如下:
/**
@param type item的唯一標(biāo)識(shí)(必填)
@param localizedTitle 是item的標(biāo)題(必填)
@param localizedSubtitle 是item的副標(biāo)題(可選)
@param icon 是item的圖標(biāo)(可選)
@param userInfo 是item所包含的信息,類型是字典(可選)
*/
- (instancetype)initWithType:(NSString *)type
localizedTitle:(NSString *)localizedTitle
localizedSubtitle:(nullable NSString *)localizedSubtitle
icon:(nullable UIApplicationShortcutIcon *)icon
userInfo:(nullable NSDictionary<NSString *, id <NSSecureCoding>> *)userInfo;
初始化生成 UIApplicationShortcutItem 對(duì)象這一步操作一般都是在 AppDelegate 類的方法里處理的,方法如下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
動(dòng)態(tài)添加時(shí),首先要判斷手機(jī)是否支持 3D Touch,代碼如下:
if (self.window.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
// TODO - 創(chuàng)建3DTouch模型
}
生成 UIApplicationShortcutItem 對(duì)象數(shù)組,添加給 UIApplication 單例對(duì)象,示例代碼如下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 首先判斷是否支持3D Touch
if (self.window.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
// 創(chuàng)建3D Touch模型
[self setup3DTouch];
}
return YES;
}
- (void)setup3DTouch {
NSMutableArray *shortcutItems = (NSMutableArray *)[UIApplication sharedApplication].shortcutItems;
UIApplicationShortcutItem *shoreItem1 = [[UIApplicationShortcutItem alloc] initWithType:@"com.xw.test1" localizedTitle:@"test1" localizedSubtitle:@"sub test1" icon:[UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeAdd] userInfo:nil];
UIApplicationShortcutItem *shortItem2 = [[UIApplicationShortcutItem alloc] initWithType:@"com.xw.test2" localizedTitle:@"test2" localizedSubtitle:@"sub test2" icon:[UIApplicationShortcutIcon iconWithTemplateImageName:@"like"] userInfo:nil];
[shortcutItems addObject:shoreItem1];
[shortcutItems addObject:shortItem2];
[UIApplication sharedApplication].shortcutItems = shortcutItems;
}
在使用動(dòng)態(tài)添加 UIApplicationShortcutItem 時(shí),可以使用自定義的圖標(biāo)(官方推薦一倍圖使用 35x35 ),也可以使用系統(tǒng)圖標(biāo),如下:
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);
3.1.3、監(jiān)聽主屏交互按鈕的點(diǎn)擊事件
設(shè)置好主屏交互的 item 后,我們剩下要做的就是在 app 內(nèi)監(jiān)聽 item 的點(diǎn)擊事件,此時(shí)需要用到之前設(shè)置 UIApplicationShortcutItemType 的唯一標(biāo)示符,通過唯一標(biāo)示符來判斷用戶點(diǎn)擊了哪個(gè) item,代碼如下:
- (void)application:(UIApplication *)application
performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem
completionHandler:(void (^)(BOOL))completionHandler {
// 不管APP在后臺(tái)還是進(jìn)程被殺死,只要通過主屏快捷操作進(jìn)來的,都會(huì)調(diào)用這個(gè)方法
NSLog(@"\n\r localizedTitle:%@ \n\r type:%@", shortcutItem.localizedTitle, shortcutItem.type);
}
3.2、預(yù)覽和跳轉(zhuǎn)
Peek and Pop 在操作上是使用一定力度按壓屏幕,觸發(fā) Peek 操作;在 Peek 狀態(tài)下,上劃喚出 Peek 快速操作( UIPreviewAction );Peek 狀態(tài)下,再次用更大力度按壓屏幕,轉(zhuǎn)場到預(yù)覽的控制器,效果如下圖:

3.2.1、Peek and Pop使用的相關(guān)說明
-
UIViewControllerPreviewingDelegate:觸發(fā)3D Touch的類,需要遵循該協(xié)議,并要實(shí)現(xiàn)協(xié)議中的方法; -
UIPreviewAction:Peek狀態(tài)下,上劃喚出快速操作選項(xiàng)實(shí)例; -
previewActionItems:Peek狀態(tài)下,上劃喚出快速操作選項(xiàng)實(shí)例數(shù)組。在要被Pop出的控制器中覆寫get方法,返回一個(gè)UIPreviewAction數(shù)組。
3.2.2、Peek and Pop使用步驟
3.2.2.1、遵循協(xié)議,并注冊代理
遵循 UIViewControllerPreviewingDelegate 協(xié)議,并在遵循 UIViewControllerPreviewingDelegate 協(xié)議的控制器中調(diào)用方法,方法如下:
- (id <UIViewControllerPreviewing>)registerForPreviewingWithDelegate:(id<UIViewControllerPreviewingDelegate>)delegate sourceView:(UIView *)sourceView;
- 遵循協(xié)議
@interface ViewController () <UIViewControllerPreviewingDelegate>
@end
- 注冊代理,并傳入響應(yīng)
3D Touch的視圖,下面是部分代碼:
// 判斷是否支持3D Touch
if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
// 注冊代理,并傳入響應(yīng)3D Touch的視圖
[self registerForPreviewingWithDelegate:self sourceView:cell];
}
3.2.2.2、 實(shí)現(xiàn)UIViewControllerPreviewingDelegate協(xié)議的兩個(gè)方法
- 當(dāng)系統(tǒng)檢測到
3D Touch時(shí),它會(huì)調(diào)用previewingContext:viewControllerForLocation代理方法,傳遞一個(gè)符合UIViewControllerPreviewing協(xié)議的previewingContext對(duì)象。使用此方法配置并返回一個(gè)視圖控制器以進(jìn)行預(yù)覽。
- (nullable UIViewController *)previewingContext:(nonnull id<UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location {
NSLog(@"%@", NSStringFromCGPoint(location));
UITableViewCell *cell = (UITableViewCell *)previewingContext.sourceView;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
XWTestViewController *vc = [[XWTestViewController alloc] init];
vc.index = (indexPath.row % 2 == 0 ? 0 : 1);
// 調(diào)整不被虛化的范圍,按壓的那個(gè)cell不被虛化(輕輕按壓時(shí)周邊會(huì)被虛化,再少用力展示預(yù)覽,再加力跳頁至設(shè)定界面)
// CGRect rect = CGRectMake(0, 0, self.view.frame.size.width,200);
// previewingContext.sourceRect = rect;
return vc;
}
- 當(dāng)系統(tǒng)察覺到足夠的壓力可以觸發(fā)
3D Touch而Pop出ViewController時(shí),會(huì)調(diào)用previewingContext:commitViewController:代理方法:
- (void)previewingContext:(id<UIViewControllerPreviewing>)previewingContext commitViewController:(UIViewController *)viewControllerToCommit {
NSLog(@"%s", __func__);
[self showViewController:viewControllerToCommit sender:self];
// [self.navigationController pushViewController:viewControllerToCommit animated:YES];
}
3.2.2.3、 添加Peek狀態(tài)下,上劃時(shí)的快速操作
如果我們想在預(yù)覽的時(shí)候,想做一些操作,那我們可以在 Peek 狀態(tài)下,添加一些 Action 或者 Group,以提供快速操作。要添加快速操作,需要在預(yù)覽控制器中重寫 - (NSArray<id<UIPreviewActionItem>> *)previewActionItems 方法,示例代碼如下:
- (NSArray<id<UIPreviewActionItem>> *)previewActionItems {
UIPreviewAction *action1 = [UIPreviewAction actionWithTitle:@"收藏" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
NSLog(@"收藏");
}];
UIPreviewAction *action2 = [UIPreviewAction actionWithTitle:@"分享" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
NSLog(@"分享");
}];
NSArray *actions = @[action1,action2];
return actions;
}
至此,3D Touch 關(guān)于 主屏交互、預(yù)覽和跳轉(zhuǎn)(Peek and Pop) 的內(nèi)容就到此結(jié)束,LivePhoto的內(nèi)容開發(fā)中很少用到,所以就略去了,后續(xù)有時(shí)間的話,會(huì)專門寫一篇關(guān)于 LivePhoto 的文章補(bǔ)上。
四、Author
如果你有什么建議,可以關(guān)注我,直接留言,留言必回。


