做者:Maxime Defauw,原文連接,原文日期:2015/11/09
譯者:saitjr;校對:numbbbbb;定稿:numbbbbbhtml
隨着 iPhone6s 與 6s plus 的到來,蘋果給咱們展示了一種全新的交互方式:重按手勢。你可能知道,這個特性已經在 Apple Watch 和 MacBook 上推出了,不過那時叫 Force Touch,就是字面上的意思,給用戶的交互添加一種新的維度。git
若是你很好奇 iPhone 的 Force Touch 爲啥要改名爲 3D Touch,那告訴你吧,you're not alone(譯者注:請用 MJ 的調子唱出來…)。不久前,以前也對這名字糾結不已的 Craig Federighi(譯者注:Apple 高級副總裁)介紹了這個新特性,第一條微博就這樣產生了。也不知道 Force Touch 這名字有啥很差的,就由於有太多星球大戰的梗?(譯者注:其實我不知道這梗...)(校對注:譯者是個妹子)(定稿注:仍是單身)github
可是,Force Touch 和 3d Touch 確實不同!Force Touch 只能識別重按。這方面 3D Touch 要靈敏多了,它可以識別按壓的力度。swift
雖說,這點不一樣看起來無足輕重,可是這使開發者能開發更多精確計量方面的 App。好比這一款名爲Gravity的應用,它利用 Force Touch 讓你的 iPhone 成爲了一個電子秤。雖然這款 App 被 Apple 拒了,可是這創意簡直太棒了。爲了展現 3D Touch 的工做流程,咱們來作一個簡單的 App。數組
先去下載這個初始案例。初始案例中只有一個空的 Single View。我在裏面建立了 App 必要的 UI 元素(UILabel
和UIImage
),並關聯了ViewController.swift
。安全
這個 App 的設計很簡單:ViewController 上有兩個 Label:一個標題和一個顯示按壓百分比的文本。app
那...開始寫代碼吧!在 iPhone6s 和 6s Plus 上,UITouch
對象多了兩個CGFloat
類型的屬性,分別是force
和maximumPossibleForce
。force
表示按得有多重,1.0
表示常規狀態的值。maximumPossibleForce
表示能承受的最大壓力值。iphone
不管什麼狀況,當用戶觸摸屏幕時,touchesBegan
方法會被調用,接着就是touchesMoved
(若是用戶手指在屏幕上滑動,那麼touchedCancelled
與TouchesEnded
也會被調用)。在這個App中,咱們只須要關注touchesMoved
方法。touchesMoved
有兩個參數:touches
和event
。touches
是一個裝着UITouch
對象的NSSet
類型集合(集合無序,而且無重複)。咱們必需要確保在touches
中只有一個UITouch
對象,但也有考慮不徹底的時候,因此強烈建議你們先利用可選綁定來判斷touches.first
(touches
中的第一個UITouch
對象)是不是空。在ViewController.swift
中添加如下代碼:ide
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) { if let touch = touches.first { if #available(iOS 9.0, *) { if traitCollection.forceTouchCapability == UIForceTouchCapability.Available { // 3D Touch capable } } } }
在這個if
判斷中,還須要添加判斷當前設備是否支持 3D Touch 的代碼。若是你只是作來玩,那就不必驗證。可是,若是是要上架的 App,那就必需要判斷,畢竟像 iPhone6 這些舊設備不支持 3D Touch。學習
除此以外,我還使用了#available
語句(Swift 2.0)對當前系統是不是 iOS9+ 作了判斷。(若是你想學習更多 Swift 2.0 相關的知識,我就更加推薦你閱讀這篇文章了。)一樣,若是你的編譯環境是 iOS9.0+,那麼這個判斷能夠省略。
要獲得按壓百分比?那太簡單了,只須要用force
屬性除以maximumPossibleForce
就能夠了(例如:touch.maximumPossibleForce
),maximumPossibleForce
表示能承受的最大壓力值。而後,更新文本:
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) { if let touch = touches.first { if #available(iOS 9.0, *) { if traitCollection.forceTouchCapability == UIForceTouchCapability.Available { // 3D Touch capable let force = touch.force/touch.maximumPossibleForce forceLabel.text = "\(force)% force" } } } }
若是你在 iPhone6s/6s Plus 上跑這個程序,按屏幕時就能看到壓力百分比了。可是,其實咱們更想知道放在 iPhone 上物體的重量,而不是百分比。根據Ryan McLeod的 App 能夠知道,傳感器的計量範圍的最大值是 385g。所以,maximumPossibleForce
就至關於 385g(至關於3.8N)。經過簡單的計算,就能夠把壓力百分比轉爲克。須要作的僅僅是用百分比*385。對於重於 385g 的物體,就把 label 改爲相似於「385+ grams」這樣的文本好了。
到此,touchesMoved
方法中的代碼更新爲:
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) { if let touch = touches.first { if #available(iOS 9.0, *) { if traitCollection.forceTouchCapability == UIForceTouchCapability.Available { if touch.force >= touch.maximumPossibleForce { forceLabel.text = "385+ grams" } else { let force = touch.force/touch.maximumPossibleForce let grams = force * 385 let roundGrams = Int(grams) forceLabel.text = "\(roundGrams) grams" } } } } }
而後...你就有了一個電子秤 App...
還有一個小問題:當物體或者觸摸事件結束以後,文本沒有重置。你能夠實現touchesEnded
方法來達到效果:
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) { forceLabel.text = "0 gram" }
另外一個 3D Touch 的用法是主屏幕上的快捷操做。快捷操做可讓用戶從快捷方式直接跳轉到 App 的某個地方。按壓 App icon 快捷方式就會出現。在介紹 3D Touch 的時候,Twitter、Instagram 等 App 就展現了這個新特性。
讓咱們來給剛纔的電子秤 App 添加一個快捷操做吧(把白色背景換成藍色)。要添加快捷操做,先打開工程目錄中的info.plist
(在導航欄上點擊工程名,在TARTGET
中找到info
選項卡)。在這個文件中,添加UIApplicationShortcutItems
數組。數組中的元素是包含一個快捷操做配置的字典:
UIApplicationShortcutItemType
(必填):快捷操做的惟一標識符(String 類型)。建議將 bundle ID 或者其餘惟一字符串做爲標識符前綴。
UIApplicationShortcutItemTitle
(必填):至關於快捷操做的 title(String 類型),用戶能夠看到。例如「顯示最近一張照片」之類的文本。
UIApplicationShortcutItemSubtitle
(可選):快捷操做的副標題(String 類型)。例如「昨天拍攝的照片」。若是你想要給快捷操做添加一個 icon,能夠自定義,也可使用系統自帶的。
UIApplicationShortcutItemIconType
(可選):表示你要選擇哪一種系統圖標做爲快捷操做的 icon(String 類型)。
UIApplicationShortcutItemIconFile
(可選):表示給快捷操做添加自定義 icon(String 類型)。
UIApplicationShortcutItemUserInfo
(可選):在快捷操做交互時傳遞的額外信息(譯者注:相似於通知的 UserInfo 參數)(Dictionary 類型)。
在這個數組中,咱們將會給自定義的快捷操做添加 4 個配置。而後,你的info.plist
文件看起來應該是這樣滴:
注意,我用到了
$(PRODUCT_BUNDLE_IDENTIFIER)
來代替com.appcoda.Scale
(就是替代的 bundle ID)。這是出於安全考慮:不管在什麼狀況下,若是我在General
中修改了 bundle ID,那整個工程的 bundle ID 就都變了,這勢必會給項目帶來不曉得影響。這樣的話,我就須要手動去修改每一個 bundle ID。在info.plist
裏面能夠看到,其實每一個 Bundle Identifier 配置項都是用的$(PRODUCT_BUNDLE_IDENTIFIER)
來表示 bundle ID 在工程中的路徑。
最後一件事,就是實現用戶觸發快捷操做的處理流程。快捷方式須要在AppDelegate.swift
的performActionForShortcutItem
方法中處理。當使用快捷操做啓動時,這個方法會被調用。因此,實現這個方法,並在方法中處理快捷操做:
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) { // Handle quick actions completionHandler(handleQuickAction(shortcutItem)) }
這個方法須要調用completionHandler
,並傳入布爾值,這個布爾值取決於快捷操做成功與否。這裏咱們封裝了一個handleQuickAction
方法來處理快捷方式。若是有多個快捷操做,最好的方式是使用枚舉,UIApplicationShortcutItemType
做爲枚舉的rawValue
(譯者注:對枚舉不熟悉能夠參考這篇文章)。定義一個枚舉,並實現handleQuickAction
方法,在方法中修改背景色爲藍色。
enum Shortcut: String { case openBlue = "OpenBlue" } func handleQuickAction(shortcutItem: UIApplicationShortcutItem) -> Bool { var quickActionHandled = false let type = shortcutItem.type.componentsSeparatedByString(".").last! if let shortcutType = Shortcut.init(rawValue: type) { switch shortcutType { case .openBlue: self.window?.backgroundColor = UIColor(red: 151.0/255.0, green: 187.0/255.0, blue: 255.0/255.0, alpha: 1.0) quickActionHandled = true } } return quickActionHandled }
一切都是這麼簡單。如今把程序跑起來,使用快捷操做來啓動 App,就能夠看到背景已是藍色了。
還有一個問題你別忘了...在程序啓動順序方面,啓動程序和使用快捷操做喚醒是有區別的。咱們都知道,程序啓動會調用willFinishLaunchingWithOptions
和didFinishLaunchingWithOptions
方法。可是當使用快捷操做喚醒時,只會觸發performActionForShortcutItem
方法(譯者注:這就意味着,使用快捷操做來啓動會走三個方法,而使用快捷操做喚醒只會走一個,具體的方法列表以下圖)。
若是你回頭看didFinishLaunchingWithOptions
方法,會發現裏面我寫了一行設置背景色爲白色的代碼。這個是在直接啓動程序時用的。
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // Override point for customization after application launch. self.window?.backgroundColor = UIColor.whiteColor() return true }
問題來了:當使用快捷操做喚醒程序時,willFinish
,didFinish
和performActionForShortcutItem
都會被調用。因此背景色會先設置成白色,接着又被設置成了藍色。顯然你不想在使用快捷操做啓動時,背景色被設置成白色。
要解決這個問題,咱們須要在didFinishLaunchingWithOptions
方法的實現中添加條件判斷:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { print("didFinishLaunchingWithOptions called") var isLaunchedFromQuickAction = false // Check if it's launched from Quick Action if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem { isLaunchedFromQuickAction = true // Handle the sortcutItem handleQuickAction(shortcutItem) } else { self.window?.backgroundColor = UIColor.whiteColor() } // Return false if the app was launched from a shortcut, so performAction... will not be called. return !isLaunchedFromQuickAction }
經過判斷可選值的UIApplicationLaunchOptionsShortcutItemKey
獲得用戶是不是經過快捷操做啓動。UIApplicationShortcutItem
能夠做爲可選值的類型。若是程序是經過快捷操做啓動的,咱們能夠直接調用handleQuickAction
方法將背景色改成藍色。
由於咱們已經在didFinishLaunchingWithOption
方法中調用了handleQuickAction
,因此不必再在performActionForShortcutItem
方法中調用一次。因此最後咱們返回了一個false
,告訴系統不要再去調用performActionForShortcutItem
方法。
再次運行程序!完美!
3D Touch 是給程序添加另外一種交互方式的好方法。可是你仍是不要忘了,目前還不是全部設備都支持 3D Touch。
經過這篇文章,你應該能給你的 App 添加快捷操做,也能計量按壓力度了。
順便,你能夠在這裏下載程序的最終版。一樣,歡迎你們留言。
本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg。