3D Touch實現(xiàn)及無限入棧bug的解決

一、簡介

3D Touch是指:通過對屏幕施加不同程度的壓力來訪問附加功能。應用可以通過顯示菜單、展示其他內容和播放動畫等形式來表現(xiàn)3D Touch,該功能從6s及其以上機型開始得到支持。

3D Touch的主要提現(xiàn)方式有三種:
  1. 主屏交互 (Home Screen Interaction)
  2. 預覽和跳轉 (Peek and Pop)
  3. LivePhoto

二、提綱

1.主屏交互 (Home Screen Interaction)
  • 靜態(tài)添加快捷操作
  • 動態(tài)添加快捷操作
2.預覽和跳轉 (Peek and Pop)
  • Peek
    1.注冊3D Touch
    2.通過代理實現(xiàn)功能
  • Pop
    1.通過代理實現(xiàn)功能

三、實現(xiàn)

1.主屏操作

3D Touch在主屏交互的表現(xiàn)形式:當用戶點擊APP的同時并施加一定壓力的時候,程序會在適當?shù)奈恢谜故境鲆粋€菜單選項列表。操作效果如下圖所示


HomeScreen展示效果

其中添加快捷操作有兩種

  • 通過 "靜態(tài)" 的方式添加快捷操作
  • 通過 "動態(tài)" 的方式添加快捷操作
    其中 "靜態(tài)" 快捷操作主要是在項目的info.plist文件中添加相關的屬性,這個網(wǎng)上資料很多,我就不介紹了。我們重點介紹動態(tài)快捷操作

1.1. 動態(tài)快捷操作

通過動態(tài)的方式添加快捷操作:這種方式主要通過代碼的形式把UIApplicationShortcutItem對象數(shù)組傳給UIApplication單例對象。我們可以在APP啟動方法

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

里面添加我們的代碼
PS: 我是為AppDelegate添加了一個叫PressTouch的分類,把所有關于3DTouch的代碼全部寫在了這個分類里,APPdelegate只需要調用.h中暴露的方法就行,算是簡化了APPDelegate中的代碼吧~~

PressTouch中.h代碼

APPDelegate直接調用

添加shortCutItem方法

附:參數(shù)對象說明
UIApplicationShortcutItem 可以看作是3D Touch點擊后,彈出菜單每行對應的模型,一行對應一個UIApplicationShortcutItem對象。

實例化方法:
- (instancetype)initWithType:(NSString *)type localizedTitle:(NSString *)localizedTitle localizedSubtitle:(nullable NSString *)localizedSubtitle icon:(nullable UIApplicationShortcutIcon *)icon userInfo:(nullable NSDictionary *)userInfo NS_DESIGNATED_INITIALIZER;

type : 對應UIApplicationShortcutItem對象的唯一標識符。
localizedTitle : 對應UIApplicationShortcutItem對象的主標題
localizedSubtitle : 對應UIApplicationShortcutItem對象的副標題
icon : 對應要顯示的圖標,有兩種圖標:

  1. 系統(tǒng)自帶的類型,代碼如下:
+ (instancetype)iconWithType:(UIApplicationShortcutIconType)type;

2.用戶自定義的圖片,代碼如下

+ (instancetype)iconWithTemplateImageName:(NSString *)templateImageName;

要注意的是,如果通過第二種自定義方式創(chuàng)建圖標,必須使用指定格式的圖片,不然顯示出來的是一片黑色。開發(fā)文檔規(guī)定格式如下:

The provided image named will be loaded from the app's bundle and will be masked to conform to the system-defined icon style.

userInfo : 主要是用來提供APP的版本信息

至此主屏幕icon上的快捷標簽創(chuàng)建就介紹完了,而他們點擊進入頁面的實現(xiàn)就有點類似消息通知的實現(xiàn)方式了,只要增加兩處代碼就好:首次啟動APP和APP沒被殺死從后臺啟動

1.2. APP沒有啟動的情況下點擊3DTouch快速啟動

APP沒有啟動,直接通過3D Touch快捷操作啟動.jpg

其中這個調用方法上圖已經給出來了,

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

就是在這個方法中調用的
其中ABGlobaiAPP是自定義的全局的視圖控制轉換器,負責處理root根視圖的切換

未啟動時視圖跳轉-1

未啟動時視圖跳轉-2

處理難點 : 這個需要判斷 "跳轉" 的界面處于tabBar的第幾個HomeController
TabBar分布圖

比如我四個一級界面分別叫做HomeViewController、MemberHomeViewController 、MessageHomeViewController、MineHomeViewController四個一級界面。

因為APP默認啟動時都是展示HomeViewController,如果選擇的是HomeViewController的子ViewController,則此時的NavigationController剛好是以HomeViewController為根視圖的,可以直接用self.navigation跳轉,但是如果點擊的是MineHomeViewController的子ViewController,則跳轉之前需要先切換tabBar的selectedViewController,找到以MineHomeViewController為根視圖的nav,然后再執(zhí)行跳轉

ps:因為這只是在程序未啟動的時候才走這個方法,所以并不存在無限壓棧的情況,所以這種解決起來也比較簡單。

1.3. APP沒被殺死,還存在于后臺,點擊3D Touch對應的跳轉

如果程序沒有被殺死,只是存活在后臺,點擊3D Touch會執(zhí)行這個方法

程序存活在后臺時,點擊3D Touch

點擊3D Touch時因為還存在于后臺,并不會走didFinishLaunchingWithOptions這個方法的,所以我采用的是通知,以通知的形式告訴APP我點擊了3D Touch,需要執(zhí)行對應的跳轉操作
APP存活時點擊3D Touch,發(fā)送通知

這里發(fā)送完通知后,需要手動調用completionHandler,告訴系統(tǒng)你已經執(zhí)行完此次操作。

這里我選擇的是在HomeViewController也就是首頁接受3D Touch的通知

- (void)pressTouchAction:(NSNotification *)notifi {
    
    if ([UserAccountModel userLogin]) { // 如果已經登錄,跳轉到對應的VC
        
        // 將要push的VC的name
        NSString *destinationVCName = [notifi.userInfo objectForKey:@"VCName"];
        
        if ([destinationVCName isEqualToString:@"MCSettingViewController"]) { // 點擊的是個人中心的設置
            
            // 先將之前的其他tabBar的跳轉到首頁(如果不這樣做,有可能上一次執(zhí)行這個方法時,首頁已經跳轉到二級界面,那個你點擊下面的tabBar時,他跳轉的是二級界面,而不是首頁)
            BaseNavigationController *homeNav = [self.tabBarController.viewControllers firstObject];
            [homeNav popToRootViewControllerAnimated:NO]; // 靜默跳轉,否則肉眼可見跳轉動畫
            
            [self.tabBarController setSelectedIndex:3]; //先跳轉tabBar
            
            // 取出以MCMineHomeViewController為根視圖的nav,以后就用這個nav去實現(xiàn)跳轉
            BaseNavigationController *nav = [self.tabBarController.viewControllers lastObject];
            
            // 當前navigationVC下的topviewController的name
            NSString *currentVCName = NSStringFromClass([nav.topViewController class]);
            
            if ([currentVCName isEqualToString:@"MCMineHomeViewController"]) {
                
                // 當前的navigationVC下的topViewController是rootVC, 可以直接push,不存在無限入棧的情況
                    
                BaseViewController *destinationVC = [NSClassFromString(destinationVCName) new];
                [nav pushViewController:destinationVC animated:YES];
                
            }else {  // 如果當前的navigationVC下的topViewController不是rootVC,pop到rootVC
                
                [nav popToRootViewControllerAnimated:NO];
                    
                BaseViewController *destinationVC = [NSClassFromString(destinationVCName) new];
                [nav pushViewController:destinationVC animated:YES];
                
            }
            
        }else { //點擊的是首頁中的幾個選項
            
            // 先將之前的其他tabBar的跳轉到首頁
            BaseNavigationController *mineNav = [self.tabBarController.viewControllers lastObject];
            [mineNav popToRootViewControllerAnimated:NO];
            
            [self.tabBarController setSelectedIndex:0];
            
            // 取出以HomeViewController為根視圖的nav,以后就用這個nav去實現(xiàn)跳轉
            BaseNavigationController *nav = [self.tabBarController.viewControllers firstObject];
            
            // 當前navigationVC下的topviewController的name
            NSString *currentVCName = NSStringFromClass([nav.topViewController class]);
            
            if ([currentVCName isEqualToString:NSStringFromClass([HomeViewController class])]) {
                
                // 當前的navigationVC下的topViewController是rootVC, 可以直接push,不存在無限入棧的情況
                
                if ([destinationVCName isEqualToString:@"QRCodeViewController"]) {
                    
                    QRCodeViewController *qrCodeVC = [[QRCodeViewController alloc] init];
                    qrCodeVC.qrcodeType = MemberDetaill;
                    [nav pushViewController:qrCodeVC animated:YES];
                    
                }else {
                    
                    BaseViewController *destinationVC = [NSClassFromString(destinationVCName) new];
                    [nav pushViewController:destinationVC animated:YES];
                    
                }
                
            }else {  // 如果當前的navigationVC下的topViewController不是rootVC,pop到rootVC
                
                [nav popToRootViewControllerAnimated:NO];
                
                if ([destinationVCName isEqualToString:@"QRCodeViewController"]) {  //如果是QRCodeViewController
                    
                    QRCodeViewController *qrCodeVC = [[QRCodeViewController alloc] init];
                    qrCodeVC.qrcodeType = MemberDetaill;
                    [nav pushViewController:qrCodeVC animated:YES];
                    
                }else {
                    
                    BaseViewController *destinationVC = [NSClassFromString(destinationVCName) new];
                    [nav pushViewController:destinationVC animated:YES];
                    
                }
                
            }
        }
        
    }else {
        
        // 如果沒有登錄,跳轉到登錄界面
        [[ABGlobaiAPP sharedInstance] gotoSiginViewController];
    }
}

處理難點

  • 跳轉時需要考慮其他界面是否處于一級界面,如果沒有則需要將其他界面先跳轉到一級界面。
  • 需要考慮到當前界面 是否是rootVC,然后處理無限入棧的問題。

2.Peek and Pop

Peek and Pop主要是通過3D Touch,使用戶可以在當前視圖預覽頁面、鏈接或者文件。如果當前頁面控制器注冊了3D Touch,我們只需要點擊相應的內容并施加一點壓力,就能使當前內容高亮,并且其他內容進入一個模糊虛化的狀態(tài);當我們再施加一點壓力,就能預覽當前內容對應的頁面;如果需要進入到該內容對應的頁面,我們只需要稍微再施加一點壓力直至預覽視圖放大到全屏,就可以跳轉到其對應的頁面。另外,如果我們在預覽頁面的同時,往上拖拽就可以顯示出一個類似UIActionsheet界面的快捷操作菜單,效果如下面幾張圖片:


輕點使當前內容高亮,并且其他內容進入一個模糊虛化的狀態(tài)

再次加大壓力,就能預覽當前內容對應的頁面

稍微再施加一點壓力直至預覽視圖放大到全屏,可以跳轉到其對應的頁面

在預覽頁面的同時,往上拖拽就可以顯示出一個類似UIActionsheet界面的快捷操作菜單

2.1. 實現(xiàn)VC1商品評論列表快速預覽VC2商品評論詳情的功能(peek)

2.1.1. 要使用3D Touch,先向要響應3D Touch功能的視圖控制器注冊3D Touch,并指定接收手勢的源視圖。

毫無疑問,要響應的視圖是TableView中的Cell。我們在Cell的初始化方法中加入以下代碼

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    ProductCommentTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ProductCommentTableViewCell" forIndexPath:indexPath];
    
    cell.model = [self.dataSource objectAtIndex:indexPath.row];
  
    // 注冊3D Touch
    /**
     從iOS9開始,我們可以通過這個類來判斷運行程序對應的設備是否支持3D Touch功能。
     
     UIForceTouchCapabilityUnknown = 0, //未知
     UIForceTouchCapabilityUnavailable = 1, //不可用
     UIForceTouchCapabilityAvailable = 2 // 可用
     
     */
    if ([self respondsToSelector:@selector(traitCollection)]) {
        
        if ([self.traitCollection respondsToSelector:@selector(forceTouchCapability)]) {
            
            if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
                
                [self registerForPreviewingWithDelegate:(id)self sourceView:cell];
            }
        }
    }
    
    return cell;
}

因為只有在6s及其以上的設備才支持3D Touch,我們可以通過UITraitCollection這個類的UITraitEnvironment協(xié)議屬性來判斷設備是否支持3D Touch。
UITraitEnvironment是UIViewController所遵守的其中一個協(xié)議,不僅包含了UI界面環(huán)境特征,而且包含了3D Touch的特征描述。

2.1.2 VC1 中實現(xiàn)UIViewControllerPreviewingDelegate代理,監(jiān)聽3D Touch手勢的觸發(fā)

示例代碼如下:

#pragma mark - UIViewControllerPreviewingDelegate
// 3D Touch時預覽的界面
- (nullable UIViewController *)previewingContext:(id <UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location {
    
    // 找到點擊的是哪個Cell
    NSIndexPath *indexPath = [self.tableView indexPathForCell:(ProductCommentTableViewCell *)[previewingContext sourceView]];
    
    // 創(chuàng)建要預覽的控制器
    GoodCommentDetailViewController *commentDetailVC = [[GoodCommentDetailViewController alloc] init];
    
    commentDetailVC.model = [self.dataSource objectAtIndex:indexPath.row];
    
    // 指定當前上下文視圖rect
    CGRect rect = CGRectMake(0, 0, kScreenWidth, 300);
    
    previewingContext.sourceRect = rect;
    
    return commentDetailVC;
}

2.2. 實現(xiàn)從VC1跳轉到VC2的功能(Pop)

// 深度按壓之后跳轉的界面
- (void)previewingContext:(id<UIViewControllerPreviewing>)previewingContext commitViewController:(UIViewController *)viewControllerToCommit {
    
    [self showViewController:viewControllerToCommit sender:self];
}

2.3. 快捷功能菜單的生成

如果我們需要在VC1快速預覽視圖出現(xiàn)時,向上拖拽得到一個快捷功能菜單,需要在VC2中實現(xiàn)以下代理方法:

// 預覽界面時需要實現(xiàn)的功能
- (NSArray<id<UIPreviewActionItem>> *)previewActionItems {
    
    UIPreviewAction *action1 = [UIPreviewAction actionWithTitle:@"選項一" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
        
    }];
    
    UIPreviewAction *action2 = [UIPreviewAction actionWithTitle:@"使用自己名字替換用戶名字" style:UIPreviewActionStyleSelected handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
       
        kWeakSelf;
        
        weakSelf.model.userName = @"濤昇依舊";
        [weakSelf.dataSource replaceObjectAtIndex:weakSelf.indexPath withObject:weakSelf.model];
        
        [ZYNotification postNotificationName:@"changeCommentUserName" object:nil];
        
    }];
    
    UIPreviewAction *action3 = [UIPreviewAction actionWithTitle:@"選項三" style:UIPreviewActionStyleDestructive handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
        
    }];
    
    return @[action1, action2, action3];
}

當然了,既然發(fā)送通知,我們就需要在商品評論列表界面接收通知,刷新界面

 [ZYNotification addObserver:self selector:@selector(reloadTableViewDataIfChangedData) name:@"changeCommentUserName" object:nil];

- (void)reloadTableViewDataIfChangedData {
    
    [self.tableView reloadData];
}

實現(xiàn)了這個代理,我們就可以在VC1中快速預覽往上拖拽得到一個快捷功能菜單。而且,我們不需要進入VC2,直接通過點擊快捷菜單的【替換該元素】這個選項,就能調用VC2替換元素的方法。應用場景:iPhone在短信列表頁面,通過快捷功能菜單快速回短信。

本文參考
  • iOS 3D Touch超詳細入門 作者:DamonMok
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,236評論 3 119
  • 一、簡介 3D Touch是指:通過對屏幕施加不同程度的壓力來訪問附加功能。應用可以通過顯示菜單、展示其他內容和播...
    DamonMok閱讀 20,864評論 11 93
  • 前言 關于3D touch蘋果官方文檔是這么開始介紹的: 大意如下:iOS9開始,所有新的手機都增加了一個三維的用...
    VV木公子閱讀 2,397評論 3 39
  • 你已經很不錯了,畢竟還有人誤叫你做經理。我聽到之后真的開心了一個星期,哈哈哈哈哈,但是我還是辭職了。 回想起上一年...
    大漠孤兒閱讀 1,133評論 0 0
  • 昨夜 我又夢見了家鄉(xiāng)的銀杏樹 夢見了你 綠的葉子 黃的葉子 飛呀飛呀 飛去了天涯海角 樹根卻賴在老地方 幾百年 幾...
    大智勿小聰閱讀 530評論 10 29

友情鏈接更多精彩內容