3D Touch是我一直想學習的功能,晚上無意間在手機上按壓了不同的應用(這么一說暴露了自己用的iPhone 6以上的設備),發現常用的應用都加了該功能。為了自己的好奇心,跟著官方文檔做了一遍,把過程中的思路和易錯點記錄下來。
目前3D Touch功能只能在iPhone6并且支持3D Touch的設備中使用
1 主屏幕Icon的3D Touch效果
對于能開啟3D Touch 的設備而言,在主屏幕中以一定的力度按壓應用的圖標,會彈出一個預覽的功能框,功能框中包括一些功能的快捷按鈕,點擊之后會啟動應用并快捷的開啟應用相關功能。具體的實現的步驟如下:
步驟一 UIApplicationShortcutItem 添加快捷按鈕
每一個快捷按鈕都是一個UIApplicationShortcutItem,可以通過兩種方法設置:
① 在應用的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效果
上圖中能清楚的看到整個過程包括兩個階段:
第一階段 :模糊化周圍,彈出一個類似彈框的界面 ----peek
第二階段:加重按壓,進入一個視圖控制器 --- pop
實現整個效果的步驟如下:
步驟一 明確哪個控件有3D Touch功能并注冊
像微博,一個cell中有兩個3D Touch控件,而且兩個控件的預覽圖也不同,并且點擊在cell別的地方時不會顯示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)
-(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的實現并不難,但是有一些細節和思路還是要好好優化,等將官方的相關文檔全部看了一遍之后,再好好總結一次。