Swift 玩轉 3D Touch 之 Peek & Pop
什麼是3D Touch
3D Touch 是iOS9之後專為 iPhone6s 機型加入的新特性,這一新技術移植於 Mac Book 上的 ForceTouch 更準確地說應該是 ForceTouch 在iPhone 上的實現吧。3D Touch 實質是一種新型的快捷單點觸控技術,在同一個點上通過不同的壓力感應觸發一種預覽行為。
在具體實現來說,3D Touch 包括以下三個技術內容:
- Peek - 輕壓專案彈出預覽視窗
- Pop - Peek 觸發之後再加力按壓預覽視窗彈出詳情視窗(相當於iOS 內的 Show Detail 行為)
- Application shortcut - 當輕壓 App 圖示時彈出的快捷選單項
當初在看WWDC之時覺得這是一個很Cool的技術,也是成為促使人們購買6s的一大理由,本以為在軟體實現上會比較麻煩,但在iOS9新的SDK中這一技術實現之簡單實在令我有點小興奮。在蘋果的開發者網站上雖然有3D Touch 的3D Touch 的示例程式碼,是一個用 UITableView
實現的一個完整示例,但這個示例過於複雜大多數程式碼都是用於處理 UITableView
的,這樣反而掩蓋了 3D Touch 實現的細節。特此,我重新寫了一個更加簡單的示例,旨在將展示如何一步步來實現 3D Touch 的功能。
Interface Builder 支援
與 Xcode 6 相比 Xcode 7 的視覺化程式設計能力有了很大幅度的提高,至少有很多地方不需要再重重複復地去Hardcode那些無聊的介面程式碼(這早該改進了,10幾年前的DELPHI早就做出了最好的視覺化設計範本)。值得稱讚的是 3D Touch 在此可以不用寫一句程式碼就能實現了!
方法極為簡單,新建一個工程,拖入一個新的 ViewController 到 IB 裡面,然後增加一個 segue 。然後在Segue的屬性中將 “Peek & Pop” 的勾選框勾上,那麼就實現 3D Touch了。是不是很簡單,很沒有技術含量?但我喜歡!因為有效率。
有圖有真相,看看下面這張圖我想只要接觸了一點一點iOS程式設計的小夥伴們都一下就能搞出來了:
如果向下深究你會發現,事情遠遠沒有這麼普通。因為通過IB我們還可以做更多的定製化。
!Storyboard Segue](http://upload-images.jianshu.io/upload_images/80120-ceb8062b08e4d16b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
這裡有一些轉義 Peek 就是 Preview , Pop 就是Commit ,這一點我們得了解的。
從上圖就可知,當將選項從"Same as Commit Segue"/"Same as Action Segue" 改成 "Custom",那麼 我們可以將Preview 與 Commit 時所採用的檢視控制器指定成特定的控制器型別,以增加更多的可自定控制,具體做法就與繫結檢視到指定控制器一樣,設定一下類名就行了,在此就不多加贅述了。
程式設計實現 3D Touch
如果你覺得上面的方法還不能滿足你的控制需要,那麼我們還可以用Hardcode的方式來實現3D Touch。隨然過程有點繁複,但對於理解3DTouch的本質卻是有著莫大的好處。
UIViewControllerPreviewingDelegate
我們只要準備兩個檢視,一個為主檢視(ViewController
)用於觸發 Peek,另一個為詳情檢視(DetailViewController
)。
在iOS 中實現Peek 與 Pop 是很簡單的,iOS9的SDK中新增加了一個叫 UIViewControllerPreviewingDelegate
的介面。只要實現這個介面就可以令我們的程式支援3D Touch 了。
import UIKit
class ViewController: UIViewController, UIViewControllerPreviewingDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// 註冊 3D Touch 的觸發檢視
if traitCollection.forceTouchCapability == .Available {
registerForPreviewingWithDelegate(self, sourceView: view)
}
}
// Mark - 實現 UIViewControllerPreviewingDelegate
func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
return nil
}
func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) {
}
}
``
3D Touch 的所有祕密都在這個 `UIViewControllerPreviewingDelegate` 介面之內了。這個介面只有兩個方法,帶有返回值的就是實現 Peek 也就是 Preview, 而沒有返回值的就是 Pop 也就是Commit, 這一點很容易理解。但有點一必須指出,那就是如果要以程式設計方式實現3DTouch那就必須在控制器載入時就將 `UIViewControllerPreviewingDelegate` 通過 `registerForPreviewingWithDelegate` 方法註冊到當前的檢視控制器中,否則是不會觸發這個介面上的事件的。
### Peek (Preview)
```swift
func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
return nil
}
如果這個方法返回 nil
那麼 Peek
行為將會失敗,iPhone 上只是會抖一下,而什麼都不會觸發的。
如果我們原來的專案中是通過表格顯示列表,點選表格單元進入詳情頁這種做法的話。這裡所返回的 controller 例項就可以是原專案的詳情頁的controller 例項。只是這個檢視不會具有 NavigationBar 和 Statusbar ,而只是 view 的一個快照。當然,我們也可以單獨為專案的預瀏行為編寫獨特的檢視控制器,在這裡直接返回控制器的例項就是了。
在我前不久的文章中曾介紹過iOS9 中的 SFSafariViewController
,那麼在今天這個示例裡面我就偷個懶,模擬在瀏覽器中的連結 Peek 行為,當使用者輕壓 示例中的 “Ray 的部落格” 字樣時就直接 Peek 一個 Safari 的瀏覽器進行預覽。
那麼我就在上述的程式碼中加入:
func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
return SFSafariViewController(URL: NSURL(string:"http://ray.dotnetage.com)!)
}
執行看效果,你會發現:無論我輕壓螢幕的哪一個位置都會彈出 Safari 瀏覽器進行預覽,而不是像我所期望的,只是在輕壓 "Ray 的部落格" 這個字樣時執行。這是因為當我們在前面採用了 registerForPreviewingWithDelegate(self, sourceView: view)
註冊3DTouch後,整個檢視的所有區域只要被Peek 都直接執行 上面的程式碼,如果我們要固定在特定的區域,那麼我們就得使用這裡的一個引數:location:CGPoint
。
通過這個引數我們就可以推測 iOS 是通過檢測我們手按壓在螢幕的哪一個具體的點上而觸發 Peek 行為的。當然了我們指壓下的範圍一定是多個點,而這個location 只是其中一個iOS認為最準確的位置而已。
要實現我的想法,只要用 CGRectContainsPoint()
方法檢查一下當前的點是不是在目標區域內,如果不是返回空就行了。那麼程式碼就可以這樣寫:
func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
return CGRectContainsPoint(peekButton.frame, location) ? SFSafariViewController(URL: NSURL(string:"http://ray.dotnetage.com")!) : nil
}
這就是最終效果:
如果我們壓下的是 table 的一個 cell 我們可以用
tableView.indexPathForRowAtPoint
方法重新獲取了對應的 cell 物件
動作選單
當我們觸發了 Peek 行為之後,將預覽檢視向上推時可以在下方出現一個與 ActionSheet 類似的選單快速操作當前預覽專案,這個功能就是 UIViewControllerPreviewingDelegate
的另一個方法 previewActionItems
。這個方法實現很簡單,只是將專案的具體操作實現為一個 UIPreviewActionItem
陣列並返回即可,這個和 ActionSheet的實現方法極為相似。
注:
previewActionItems
是一個UIViewController
的新增方法
以下程式碼示例是生成一個帶有“刪除”和“完成”的選單項:
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]
}
}
執行效果如下:
好了對於Peek的控制就是這麼多,但此時如果我們重壓觸發 Pop 會馬上什麼都沒有了!為什麼呢?因為我們還有另一個方法還沒有實現呢,這個方法實現起來也很簡單,直接用 showViewController()
方法將viewControllerToCommit
這個引數給顯示出來就是了,從這裡我們就可以得出這樣的結論了,peek 被執行後所返回的 控制器例項將會被傳遞到 這個方法的 commitViewController
中。
具體程式碼實現如下:
func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) {
showViewController(viewControllerToCommit, sender: self)
}
That's all! 將以上的程式碼寫好 Load 到我們的手機上就可以體驗這個小示例了。有一點比較遺憾的是,模擬器是不能模擬 3D Touch 的,只能在真機上除錯,希望 Apple 會在下一個版本的 XCode 中加入3D Touch 的模擬器支援。
最後,奉上本文章的完整程式碼示例: https://github.com/DotNetAge/PeepPopExample
小結
關於 Application shortcut 是個比較蛋痛的功能,雖然它能支援靜態與動態選單,但是寫起來內容也不少,所以今天還是打算先寫到這裡。以後再找時間專門寫一篇關於 Application shortcut 的文章吧。
相關文章
- 玩轉swift — UIKit 之 UIView(1)SwiftUIView
- Swift如何給應用新增3D Touch選單Swift3D
- iOS之實現3D TouchiOS3D
- 玩轉iOS開發:Touch ID整合iOS
- 玩轉linux命令(8):touch命令Linux
- Android手機的 3D Touch怎麼玩兒?Android3D
- 3D Touch 詳解3D
- 3D Touch 實現3D
- 看看這些3D Touch技巧 你真的用好3D Touch了嗎?3D
- 玩轉3D Swiper性感秀之思路分析總結3D
- Swift實現Touch ID驗證Swift
- 3D Touch 簡單應用3D
- iPhone8開啟和關閉3D Touch教程 3D touch怎麼設定iPhone3D
- 蘋果ios10 3d touch怎麼用 ios10的3d touch使用教程蘋果iOS3D
- iOS人機介面指南-3D TouchiOS3D
- 3d touch怎麼清理通知訊息 3d touch一鍵清理通知訊息方法詳解3D
- 帶你玩轉css3的3D!CSSS33D
- Instagram 的 3D Touch 經驗談3D
- 玩轉 JavaScript 之詳解 thisJavaScript
- linux命令之touchLinux
- [譯] 蘋果公司如何修復 3D Touch蘋果3D
- PHP審計之POP鏈挖掘PHP
- pop3命令簡介(轉)
- 大牛帶你玩轉 CSS3 3D 技術CSSS33D
- 微軟Surface Phone概念機再爆 支援3D Touch微軟3D
- 玩轉Redis叢集之SentinelRedis
- 玩轉Redis叢集之CodisRedis
- 玩轉Redis叢集之ClusterRedis
- iOS開發之玩轉字串iOS字串
- 劍指蘋果 安卓7.0確定將支援3D Touch蘋果安卓3D
- Swift 屬性字怎麼玩Swift
- 玩轉Android Jetpack系列之ViewModeAndroidJetpackView
- 玩轉 JavaScript 之資料型別JavaScript資料型別
- 玩轉docker之自定義PHP容器DockerPHP
- 玩轉 Stack Overflow 之提問篇
- 玩轉Vue3之ComposablesVue
- Stream中的Peek操作
- C# peek()的用法C#