3D Touch介紹:電子秤App與快捷操做

做者: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 元素(UILabelUIImage),並關聯了ViewController.swift安全

這個 App 的設計很簡單:ViewController 上有兩個 Label:一個標題和一個顯示按壓百分比的文本。app

那...開始寫代碼吧!在 iPhone6s 和 6s Plus 上,UITouch對象多了兩個CGFloat類型的屬性,分別是forcemaximumPossibleForceforce表示按得有多重,1.0表示常規狀態的值。maximumPossibleForce表示能承受的最大壓力值。iphone

不管什麼狀況,當用戶觸摸屏幕時,touchesBegan方法會被調用,接着就是touchesMoved(若是用戶手指在屏幕上滑動,那麼touchedCancelledTouchesEnded也會被調用)。在這個App中,咱們只須要關注touchesMoved方法。touchesMoved有兩個參數:toucheseventtouches是一個裝着UITouch對象的NSSet類型集合(集合無序,而且無重複)。咱們必需要確保在touches中只有一個UITouch對象,但也有考慮不徹底的時候,因此強烈建議你們先利用可選綁定來判斷touches.firsttouches中的第一個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.swiftperformActionForShortcutItem方法中處理。當使用快捷操做啓動時,這個方法會被調用。因此,實現這個方法,並在方法中處理快捷操做:

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,就能夠看到背景已是藍色了。

還有一件事

還有一個問題你別忘了...在程序啓動順序方面,啓動程序和使用快捷操做喚醒是有區別的。咱們都知道,程序啓動會調用willFinishLaunchingWithOptionsdidFinishLaunchingWithOptions方法。可是當使用快捷操做喚醒時,只會觸發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
}

問題來了:當使用快捷操做喚醒程序時,willFinishdidFinishperformActionForShortcutItem都會被調用。因此背景色會先設置成白色,接着又被設置成了藍色。顯然你不想在使用快捷操做啓動時,背景色被設置成白色。

要解決這個問題,咱們須要在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

相關文章
相關標籤/搜索