Swift 玩轉(zhuǎn) 3D Touch 之 Peek & Pop

apple_3dtouch.png

什么是3D Touch

3D Touch 是iOS9之后專為 iPhone6s 機(jī)型加入的新特性,這一新技術(shù)移植于 Mac Book 上的 ForceTouch 更準(zhǔn)確地說應(yīng)該是 ForceTouch 在iPhone 上的實(shí)現(xiàn)吧。3D Touch 實(shí)質(zhì)是一種新型的快捷單點(diǎn)觸控技術(shù),在同一個(gè)點(diǎn)上通過不同的壓力感應(yīng)觸發(fā)一種預(yù)覽行為。

在具體實(shí)現(xiàn)來說,3D Touch 包括以下三個(gè)技術(shù)內(nèi)容:

  • Peek - 輕壓項(xiàng)目彈出預(yù)覽窗口
  • Pop - Peek 觸發(fā)之后再加力按壓預(yù)覽窗口彈出詳情窗口(相當(dāng)于iOS 內(nèi)的 Show Detail 行為)
  • Application shortcut - 當(dāng)輕壓 App 圖標(biāo)時(shí)彈出的快捷菜單項(xiàng)

當(dāng)初在看WWDC之時(shí)覺得這是一個(gè)很Cool的技術(shù),也是成為促使人們購買6s的一大理由,本以為在軟件實(shí)現(xiàn)上會(huì)比較麻煩,但在iOS9新的SDK中這一技術(shù)實(shí)現(xiàn)之簡(jiǎn)單實(shí)在令我有點(diǎn)小興奮。在蘋果的開發(fā)者網(wǎng)站上雖然有3D Touch 的3D Touch 的示例代碼,是一個(gè)用 UITableView 實(shí)現(xiàn)的一個(gè)完整示例,但這個(gè)示例過于復(fù)雜大多數(shù)代碼都是用于處理 UITableView 的,這樣反而掩蓋了 3D Touch 實(shí)現(xiàn)的細(xì)節(jié)。特此,我重新寫了一個(gè)更加簡(jiǎn)單的示例,旨在將展示如何一步步來實(shí)現(xiàn) 3D Touch 的功能。

Interface Builder 支持

與 Xcode 6 相比 Xcode 7 的可視化編程能力有了很大幅度的提高,至少有很多地方不需要再重重復(fù)復(fù)地去Hardcode那些無聊的界面代碼(這早該改進(jìn)了,10幾年前的DELPHI早就做出了最好的可視化設(shè)計(jì)范本)。值得稱贊的是 3D Touch 在此可以不用寫一句代碼就能實(shí)現(xiàn)了!

方法極為簡(jiǎn)單,新建一個(gè)工程,拖入一個(gè)新的 ViewController 到 IB 里面,然后增加一個(gè) segue 。然后在Segue的屬性中將 “Peek & Pop” 的勾選框勾上,那么就實(shí)現(xiàn) 3D Touch了。是不是很簡(jiǎn)單,很沒有技術(shù)含量?但我喜歡!因?yàn)橛行省?/p>

有圖有真相,看看下面這張圖我想只要接觸了一點(diǎn)一點(diǎn)iOS編程的小伙伴們都一下就能搞出來了:

Interface Builder

如果向下深究你會(huì)發(fā)現(xiàn),事情遠(yuǎn)遠(yuǎn)沒有這么普通。因?yàn)橥ㄟ^IB我們還可以做更多的定制化。

!Storyboard Segue](http://upload-images.jianshu.io/upload_images/80120-ceb8062b08e4d16b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

這里有一些轉(zhuǎn)義 Peek 就是 Preview , Pop 就是Commit ,這一點(diǎn)我們得了解的。

從上圖就可知,當(dāng)將選項(xiàng)從"Same as Commit Segue"/"Same as Action Segue" 改成 "Custom",那么 我們可以將Preview 與 Commit 時(shí)所采用的視圖控制器指定成特定的控制器類型,以增加更多的可自定控制,具體做法就與綁定視圖到指定控制器一樣,設(shè)置一下類名就行了,在此就不多加贅述了。

編程實(shí)現(xiàn) 3D Touch

如果你覺得上面的方法還不能滿足你的控制需要,那么我們還可以用Hardcode的方式來實(shí)現(xiàn)3D Touch。隨然過程有點(diǎn)繁復(fù),但對(duì)于理解3DTouch的本質(zhì)卻是有著莫大的好處。

UIViewControllerPreviewingDelegate

我們只要準(zhǔn)備兩個(gè)視圖,一個(gè)為主視圖(ViewController)用于觸發(fā) Peek,另一個(gè)為詳情視圖(DetailViewController)。

在iOS 中實(shí)現(xiàn)Peek 與 Pop 是很簡(jiǎn)單的,iOS9的SDK中新增加了一個(gè)叫 UIViewControllerPreviewingDelegate 的接口。只要實(shí)現(xiàn)這個(gè)接口就可以令我們的程序支持3D Touch 了。


import UIKit

class ViewController: UIViewController, UIViewControllerPreviewingDelegate {
        
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 注冊(cè) 3D Touch 的觸發(fā)視圖
        if traitCollection.forceTouchCapability == .Available {
            registerForPreviewingWithDelegate(self, sourceView: view)
        }
    }


    // Mark - 實(shí)現(xiàn) UIViewControllerPreviewingDelegate

    func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {        
        return nil
    }
    
    func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) {
    }
}

``


3D Touch 的所有秘密都在這個(gè) `UIViewControllerPreviewingDelegate` 接口之內(nèi)了。這個(gè)接口只有兩個(gè)方法,帶有返回值的就是實(shí)現(xiàn) Peek 也就是 Preview, 而沒有返回值的就是 Pop 也就是Commit, 這一點(diǎn)很容易理解。但有點(diǎn)一必須指出,那就是如果要以編程方式實(shí)現(xiàn)3DTouch那就必須在控制器加載時(shí)就將 `UIViewControllerPreviewingDelegate` 通過 `registerForPreviewingWithDelegate` 方法注冊(cè)到當(dāng)前的視圖控制器中,否則是不會(huì)觸發(fā)這個(gè)接口上的事件的。


### Peek (Preview)


```swift
func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {        
        return nil
}

如果這個(gè)方法返回 nil 那么 Peek 行為將會(huì)失敗,iPhone 上只是會(huì)抖一下,而什么都不會(huì)觸發(fā)的。

如果我們?cè)瓉淼捻?xiàng)目中是通過表格顯示列表,點(diǎn)擊表格單元進(jìn)入詳情頁這種做法的話。這里所返回的 controller 實(shí)例就可以是原項(xiàng)目的詳情頁的controller 實(shí)例。只是這個(gè)視圖不會(huì)具有 NavigationBar 和 Statusbar ,而只是 view 的一個(gè)快照。當(dāng)然,我們也可以單獨(dú)為項(xiàng)目的預(yù)瀏行為編寫?yīng)毺氐囊晥D控制器,在這里直接返回控制器的實(shí)例就是了。

在我前不久的文章中曾介紹過iOS9 中的 SFSafariViewController ,那么在今天這個(gè)示例里面我就偷個(gè)懶,模擬在瀏覽器中的鏈接 Peek 行為,當(dāng)用戶輕壓 示例中的 “Ray 的博客” 字樣時(shí)就直接 Peek 一個(gè) Safari 的瀏覽器進(jìn)行預(yù)覽。

那么我就在上述的代碼中加入:

func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
        
    return  SFSafariViewController(URL: NSURL(string:"http://ray.dotnetage.com)!)
}

運(yùn)行看效果,你會(huì)發(fā)現(xiàn):無論我輕壓屏幕的哪一個(gè)位置都會(huì)彈出 Safari 瀏覽器進(jìn)行預(yù)覽,而不是像我所期望的,只是在輕壓 "Ray 的博客" 這個(gè)字樣時(shí)執(zhí)行。這是因?yàn)楫?dāng)我們?cè)谇懊娌捎昧?registerForPreviewingWithDelegate(self, sourceView: view)
注冊(cè)3DTouch后,整個(gè)視圖的所有區(qū)域只要被Peek 都直接執(zhí)行 上面的代碼,如果我們要固定在特定的區(qū)域,那么我們就得使用這里的一個(gè)參數(shù):location:CGPoint。

通過這個(gè)參數(shù)我們就可以推測(cè) iOS 是通過檢測(cè)我們手按壓在屏幕的哪一個(gè)具體的點(diǎn)上而觸發(fā) Peek 行為的。當(dāng)然了我們指壓下的范圍一定是多個(gè)點(diǎn),而這個(gè)location 只是其中一個(gè)iOS認(rèn)為最準(zhǔn)確的位置而已。

要實(shí)現(xiàn)我的想法,只要用 CGRectContainsPoint() 方法檢查一下當(dāng)前的點(diǎn)是不是在目標(biāo)區(qū)域內(nèi),如果不是返回空就行了。那么代碼就可以這樣寫:

func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
        
    return CGRectContainsPoint(peekButton.frame, location) ? SFSafariViewController(URL: NSURL(string:"http://ray.dotnetage.com")!) : nil
}

這就是最終效果:

預(yù)覽

如果我們壓下的是 table 的一個(gè) cell 我們可以用 tableView.indexPathForRowAtPoint 方法重新獲取了對(duì)應(yīng)的 cell 對(duì)象

動(dòng)作菜單

當(dāng)我們觸發(fā)了 Peek 行為之后,將預(yù)覽視圖向上推時(shí)可以在下方出現(xiàn)一個(gè)與 ActionSheet 類似的菜單快速操作當(dāng)前預(yù)覽項(xiàng)目,這個(gè)功能就是 UIViewControllerPreviewingDelegate 的另一個(gè)方法 previewActionItems 。這個(gè)方法實(shí)現(xiàn)很簡(jiǎn)單,只是將項(xiàng)目的具體操作實(shí)現(xiàn)為一個(gè) UIPreviewActionItem 數(shù)組并返回即可,這個(gè)和 ActionSheet的實(shí)現(xiàn)方法極為相似。

注:previewActionItems 是一個(gè) UIViewController 的新增方法

以下代碼示例是生成一個(gè)帶有“刪除”和“完成”的菜單項(xiàng):

extension SFSafariViewController {
    
    override public func previewActionItems() -> [UIPreviewActionItem] {
        
        let deleteAction =  UIPreviewAction(title: "刪除",
            style: UIPreviewActionStyle.Destructive,
            handler: {
                (previewAction,viewController) in
                
                NSLog("Delete")
                
        })
        
        let doneAction = UIPreviewAction(title: "完成",
            style: UIPreviewActionStyle.Default,
            handler: {
                (previewActin, viewController) in
                
                NSLog("Done")
        })
        
        return [doneAction, deleteAction]
    }
    
}

運(yùn)行效果如下:

Peek

好了對(duì)于Peek的控制就是這么多,但此時(shí)如果我們重壓觸發(fā) Pop 會(huì)馬上什么都沒有了!為什么呢?因?yàn)槲覀冞€有另一個(gè)方法還沒有實(shí)現(xiàn)呢,這個(gè)方法實(shí)現(xiàn)起來也很簡(jiǎn)單,直接用 showViewController()方法將viewControllerToCommit這個(gè)參數(shù)給顯示出來就是了,從這里我們就可以得出這樣的結(jié)論了,peek 被執(zhí)行后所返回的 控制器實(shí)例將會(huì)被傳遞到 這個(gè)方法的 commitViewController 中。

具體代碼實(shí)現(xiàn)如下:

func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) {
    showViewController(viewControllerToCommit, sender: self)
}

That's all! 將以上的代碼寫好 Load 到我們的手機(jī)上就可以體驗(yàn)這個(gè)小示例了。有一點(diǎn)比較遺憾的是,模擬器是不能模擬 3D Touch 的,只能在真機(jī)上調(diào)試,希望 Apple 會(huì)在下一個(gè)版本的 XCode 中加入3D Touch 的模擬器支持。

最后,奉上本文章的完整代碼示例: https://github.com/DotNetAge/PeepPopExample

小結(jié)

關(guān)于 Application shortcut 是個(gè)比較蛋痛的功能,雖然它能支持靜態(tài)與動(dòng)態(tài)菜單,但是寫起來內(nèi)容也不少,所以今天還是打算先寫到這里。以后再找時(shí)間專門寫一篇關(guān)于 Application shortcut 的文章吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,692評(píng)論 4 61
  • 在前面幾期快報(bào)中,我們介紹了 3D Touch 的基本內(nèi)容,以及如何創(chuàng)建 3D Touch 桌面快捷菜單。這次咱們...
    SwiftCafe閱讀 1,234評(píng)論 0 9
  • 前言 關(guān)于這篇文章 由于iPhone 6S發(fā)布不到一年的時(shí)間,很多新特性、新技術(shù)還未普遍,不管是3D Touch的...
    Tangentw閱讀 4,748評(píng)論 8 18
  • 在微博偶然看到不少人在傳播某某網(wǎng)站可以查詢密碼泄露,甚至開房信息。 循著微博找到了那網(wǎng)站,輸入常用的郵箱,結(jié)果出來...
    一片云閱讀 286評(píng)論 0 1
  • 讀的什么書:《養(yǎng)育的選擇》 閱讀有效時(shí)間:半小時(shí) 閱讀中遇到了什么困難:這本書讀起來非常容易,和小強(qiáng)升職記不同,教...
    陳小丁閱讀 968評(píng)論 0 0

友情鏈接更多精彩內(nèi)容