隨著iPhone 6s 和 iPhone 6s Plus 的釋出,蘋果開始新增了一種全新的觸控方式——3D Touch。最近幾天簡單地研究了一下,跟大家分享一下我的一些經驗。
##UIApplicationShortcutItems
當你重按應用的圖示時,會彈出類似這樣的小選單(以微信為例):
新增這樣的快捷選單主要有 靜態 和 動態 兩種方法: ####靜態方法 參看 UIApplicationShortcutItems-蘋果官方文件
可以看到 UIApplicationShortcutItemTitle
和 UIApplicationShortcutItemType
這兩個變數是必須的。
我們在專案的 info.plist
檔案中新增如下資訊:
執行結果:
####動態方法 動態方法是在專案中新增程式碼:
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
新增這樣兩個屬性:
我們建立一個繼承於 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 是在被預覽的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;
}
複製程式碼
通過返回 UIPreviewAction
或 UIPreviewActionGroup
組成的陣列實現。
一個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 歡迎大家交流討論。