置頂文章

iOS13 - force touch peek&pop 功能移轉到 menuContextInteraction

2019/12/09 09:29

Tags: iOS 3D touch iphone

3D Touch 走入歷史

2019 年蘋果發表了 iPhone 11 系列,全面從硬體層拔除了 3D touch 支援,不論是 iPhone 11 還是 iPhone 11 pro 都沒有搭載 3D Touch 模組,正式宣告 3D touch 即將走入歷史。

這個改動也在 iOS 上有相對應的痕跡。

可以看到的有,當你將手上的 iPhone 升級到 iOS13時,就算是有 3D touch 模組的 iPhone 6s ~ iPhone XS 系列,在使用原生系統操作,例如桌面按壓、鍵盤重壓、網頁重壓等等過去的 3D touch 操作,會感覺到使用體驗已經變成了新版的 Haptic Touch 觸感。

但有些人可能發現,在使用第三方 app 時還是可以使用 3D touch 功能,那是因為 iOS SDK 的 3D touch API 只有被註銷,還未被完全拔除,所以第三方 app 還可以繼續使用 3D touch 功能。

說好的只是 deprecated 呢?

上面這些都是在 iOS 13.2.3 出來之前的美好想像 — 好啊蘋果自己要拔沒關係,我的 APP 繼續支援,看能支援到哪裡算哪裡,總可以吧。

但是,這次 iOS 13.2.3 出來之後,大家可以發現 iOS 13.2.3 的「3D Touch 硬體支援 iPhone」也無法在第三方 app 中使用 3D touch 功能了,登愣。


Peek & Pop API deprecated in iOS 13.0

previewingContext:viewControllerForLocation:

Declaration The view controller whose view you want to provide as the preview (peek), or to disable preview.. Implement…developer.apple.com

- (nullable UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location API_DEPRECATED_WITH_REPLACEMENT("UIContextMenuInteraction", ios(9.0, 13.0));


https://developer.apple.com/documentation/uikit/uiviewcontrollerpreviewingdelegate/1621366-previewingcontext?language=objc

- (void)previewingContext:(id <UIViewControllerPreviewing>)previewingContext commitViewController:(UIViewController *)viewControllerToCommit API_DEPRECATED_WITH_REPLACEMENT("UIContextMenuInteraction", ios(9.0, 13.0));



Migration 功能搬遷

可以看到文件裡面引導我們去使用一個叫做 contextMenuInteraction 的 API,他可以說是蘋果為 iOS 13+ 的 APP 提供 peek&pop 的新解決方案,使用方式也比過去的 peek&pop API 要簡單上許多。


contextMenuInteraction 的使用方式有兩種:手動為需要支援的 view 註冊,或是直接加在 TableView / CollectionView 下


方法一:手動註冊

手動生成 UIContextMenuInteraction ,並註冊 delegate

UIContextMenuInteraction *interaction = [[UIContextMenuInteraction alloc] initWithDelegate:self];

[self.myView addInteraction:interaction];


實作 delegate contextMenuInteraction: configurationForMenuAtLocation:

-(UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction *)interaction configurationForMenuAtLocation:(CGPoint)location API_AVAILABLE(ios(13.0))

{

    return nil;

}


他需要我們回傳一個 UIContextMenuConfiguration ,這個 configuration 會定義當這個 view 被 haptic 叫起時出現的 menu 長得怎麼樣


-(UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction *)interaction configurationForMenuAtLocation:(CGPoint)location API_AVAILABLE(ios(13.0)){

    //1. 這個 API 需要回傳一個 UIContextMenuInteraction

    UIContextMenuConfiguration *config = [UIContextMenuConfiguration configurationWithIdentifier:nil previewProvider:^UIViewController * _Nullable{

        //2. 當這個 view 被叫起時,要預覽哪個話面。

        //將要預覽的 viewController 回傳,如果不要客製化預覽畫面,可以回傳 nil 或是整個 previewProvider 代入 nil 即可

        return nil;

    } actionProvider:^UIMenu * _Nullable(NSArray<UIMenuElement *> * _Nonnull suggestedActions) {

        //3. 這邊定義出來的 menu 有哪些按鈕,由 UIAction 和 UIMenu 組成,最後將 UIMenu 回傳

        NSArray *buttons = @[];

        UIAction *action = [UIAction actionWithTitle:@"Button Title" image:nil identifier:nilhandler:^(__kindof UIAction * _Nonnull action) {

            //do something

        }];

        buttons = [buttons arrayByAddingObject:action];

        UIMenu *menu = [UIMenu menuWithTitle:@"My Custom Menu Title" image:nil identifier:niloptions:0 children:buttons];

        return menu;

    }];


    return config;

}


到這邊就完成一個客製化的類似peek&pop 功能。

如果是要放到 TableView 或是 CollectionView 上,又更簡單了。


方法二、在 UITableViewController / UICollectionViewController 中實作 menu 功能

下面以 TableViewController 為例,UICollectionViewController 也有對應的 API 。

要在 TableViewController 實作 ContextMenuInteraction 又更容易了。


在 iOS 13 以上,UITableViewDelegate 直接送你一個 function 告訴你使用者在哪個 indexPath 做了 Haptic Touch 操作並嘗試叫出 Menu

- (nullable UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(watchos, tvos);



這隻 API 直接告訴你使用者在哪個位置做了動作,給你 IndexPath 和 Point。

//1. 記得加上 API_AVAILABLE(iOS(13.0))

-(UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point API_AVAILABLE(ios(13.0)){

    UITableViewCell *cell = [self.mainTableView cellForRowAtIndexPath:indexPath];

    if(cell == self.adCell_trekSuprAd ||

       cell == self.adCell_mopubNative ||

       cell == self.adCell_trekNativeAd){

        return nil;

    }

    

    postObject *post = [self.arrayOfPosts objectAtIndex:indexPath.row];

    

    UIContextMenuConfiguration *config = [UIContextMenuConfiguration configurationWithIdentifier:nil previewProvider:^UIViewController * _Nullable{

         //2. 要 preview 的 viewController

          UIViewController *previewingVC = ...

          return previewingVC;

    } actionProvider:^UIMenu * _Nullable(NSArray<UIMenuElement *> * _Nonnull suggestedActions) {

        NSArray *buttons = @[];

        UIAction *openPost = [UIAction actionWithTitle:@"展開" image:nil identifier:nilhandler:^(__kindof UIAction * _Nonnull action) {

            UIViewController *nextViewController = ...

            [self.navigationController pushViewController: nextViewController animated:YES];

        }];

        

        UIAction *archive = [UIAction actionWithTitle:@"收藏" image:nil identifier:nilhandler:^(__kindof UIAction * _Nonnull action) {

                //do archive

            }];

        

        

        buttons = [buttons arrayByAddingObject:openPost];

        buttons = [buttons arrayByAddingObject:archive];

        UIMenu *menu = [UIMenu menuWithTitle:@"" image:nil identifier:nil options:0 children:buttons];

        return menu;

    }];

    return config;

}




到這邊就做好了 Menu 選單的功能啦。



總結

雖然 peek & pop API 提早被拉掉了很可惜,但是新的 contextMenuInteraction API 寫起來真的比以前的 previewingContext + CommitViewController 方便很多。


以前 API 只會告訴你使用者在哪個位置嘗試按壓 force touch,開發者還要自己從 point 換算成實際點了哪個 view,再回傳告訴系統要讓哪個 view 呈現「浮起來」的感覺。更別提 commit 時還會有一些 presenting/presented view controller 的 edge case 要避免的問題了。


現在這隻 API 是以直接掛在 view 上面的方式進行,同時也整合進 UITableViewController / UICollectionViewController delegates 中,開發者只要專心在可以客製化的項目上,其餘的 UI 效果則交給系統來處理。


話雖如此,身為原廠 3D touch 手感愛用者的小編而言,這樣的改動可說是喜憂參半。

成效數據

共 NT$ 金額

評分與評論

5.0 滿分五顆星

1 份評分

贊助方案

$ 1000

已售完

10/29(四) 我要一起跟辣机一起組鋼彈!

$ 隨喜贊助

喝杯咖啡

Supr.One by Aotter Inc.