Typist:iOS上簡潔的鍵盤監(jiān)聽庫

前言

在iOS想要監(jiān)聽鍵盤的話是通過注冊通知、接收鍵盤發(fā)來的通知實(shí)現(xiàn)的,雖然步驟不是很多,不過由于鍵盤的狀態(tài)很多(有6種),如果需求要監(jiān)聽所有的狀態(tài),想想你要把相同的代碼寫6次,是有多煩人。而且適配的系統(tǒng)在iOS 9以下還要把一個(gè)個(gè)通知移除。這是有多不簡潔...不過自從我遇到了Typist, 感覺優(yōu)雅簡潔到要死。

Typist簡介

Typist is a small, drop-in Swift UIKit keyboard manager for iOS apps. It helps you manage keyboard's screen presence and behavior without notification center and Objective-C.

也就是說Typist主要方便了鍵盤顯示的事件,那么他究竟是怎么方便的呢?下面這段代碼就是Typist的簡單使用:

 let keyboard = Typist.shared
keyboard
    .on(event: .didShow) { (options) in
        print("New Keyboard Frame is \(options.endFrame).")
    }
    .on(event: .didHide) { (options) in
        print("It took \(options.animationDuration) seconds to animate keyboard out.")
    }
    .start()

Oh, my god.怎么看上去和Rx有點(diǎn)像呀。哈哈,確實(shí)是的,不過這里作者只是運(yùn)用了方法的鏈?zhǔn)秸{(diào)用。并沒有Rx那么高大上哈。
如何你想要移除鍵盤監(jiān)聽,那么直接調(diào)用keyboard.clear()即可。
不過這里有點(diǎn)需要注意下,當(dāng)有多個(gè)不同的對象都要監(jiān)聽鍵盤時(shí),不要使用該單例。也就說這種情況下就需要你自己去創(chuàng)建Typist對象了,不要使用Typist.shared這個(gè)會導(dǎo)致你其中某些對象接受不到通知。

從鍵盤監(jiān)聽說起

在不使用Typist的情況下,我們是怎么做鍵盤監(jiān)聽的呢?


具體步驟如圖所示。不過在如果你的應(yīng)用支持iOS 9+以上,那么就可以不用去移除通知,這個(gè)在Apple文檔里也有說明。
可能只是一張圖你看不出來有多煩人,那么我用代碼演示下是這樣的。

而且這僅僅只是監(jiān)聽了鍵盤的實(shí)現(xiàn)方法,就已經(jīng)需要這么多代碼了。那么我們要是遇到需求要監(jiān)聽所有的鍵盤通知呢?那么上面代碼就成了這樣:

一堆樣式相同的模板代碼,有人會說,我需要一個(gè)函數(shù)封裝下,不過還是還是不夠Typist簡潔。

那么Typiest是如何做鍵盤監(jiān)聽的

let keyboard = Typist.shared // #1
 
keyboard
    .on(event: .didShow) { (options) in // #2
        print("New Keyboard Frame is \(options.endFrame).")
    }
    .on(event: .didHide) { (options) in // #3
        print("It took \(options.animationDuration) seconds to animate keyboard out.")
    }
    .start() // #4

還是從簡介中的這段代碼說起,#1處是使用單例創(chuàng)建一個(gè)Typist對象;#2 處監(jiān)聽了鍵盤顯示結(jié)束,閉包回調(diào)里的options包含了一些鍵盤信息,也就是NotificationCenter里面的info信息;#3 處監(jiān)聽了鍵盤的隱藏結(jié)束,閉包回調(diào)同上;#4 在這里開始監(jiān)聽,相當(dāng)與我們使用的addobserver: 方法。
具體的邏輯如下圖所示:


那么它內(nèi)部究竟是怎么做的呢?

public func on(event: KeyboardEvent, do callback: TypistCallback?) -> Self 方法

這個(gè)方式的實(shí)現(xiàn)很簡單,僅僅是將callbac回調(diào)放入一個(gè)dict里面,dict的key是event,然后返回自己。

public func on(event: KeyboardEvent, do callback: TypistCallback?) -> Self {
        callbacks[event] = callback
        return self
}

KeyboardEvent

作者在這里定義了一個(gè)枚舉來封裝了各個(gè)不同的鍵盤監(jiān)聽,然后我們在on方法里直接用傳入枚舉值就可以。

public enum KeyboardEvent {
    /// Event raised by UIKit's `.UIKeyboardWillShow`.
    case willShow
        
    /// Event raised by UIKit's `.UIKeyboardDidShow`.
    case didShow
        
    /// Event raised by UIKit's `.UIKeyboardWillShow`.
    case willHide
        
    /// Event raised by UIKit's `.UIKeyboardDidHide`.
    case didHide
        
    /// Event raised by UIKit's `.UIKeyboardWillChangeFrame`.
    case willChangeFrame
        
    /// Event raised by UIKit's `.UIKeyboardDidChangeFrame`.
    case didChangeFrame
}

start 方法

start在這里僅僅是注冊了鍵盤通知,注意下addObserver方法的參數(shù)。這里的event就是上面的KeyboardEvent,通過event映射到NSNotification.Name 和 SEL。

/// Starts listening to events and calling corresponding events handlers.
    public func start() {
        let center = NotificationCenter.`default`
        
        for event in callbacks.keys {
            center.addObserver(self, selector: event.selector, name: event.notification, object: nil)
        }
    }

event.selector 與 event.notification 屬性

這兩個(gè)KeyboardEvent擴(kuò)展下的私有屬性分別返回上面提到的 NSNotification.Name 和 SEL

fileprivate extension Typist.KeyboardEvent {
    var notification: NSNotification.Name {
        get {
            switch self {
            case .willShow:
                return .UIKeyboardWillShow
            case .didShow:
                return .UIKeyboardDidShow
            case .willHide:
                return .UIKeyboardWillHide
            case .didHide:
                return .UIKeyboardDidHide
            case .willChangeFrame:
                return .UIKeyboardWillChangeFrame
            case .didChangeFrame:
                return .UIKeyboardDidChangeFrame
            }
        }
    }
    
    var selector: Selector {
        get {
            switch self {
            case .willShow:
                return #selector(Typist.keyboardWillShow(note:))
            case .didShow:
                return #selector(Typist.keyboardDidShow(note:))
            case .willHide:
                return #selector(Typist.keyboardWillHide(note:))
            case .didHide:
                return #selector(Typist.keyboardDidHide(note:))
            case .willChangeFrame:
                return #selector(Typist.keyboardWillChangeFrame(note:))
            case .didChangeFrame:
                return #selector(Typist.keyboardDidChangeFrame(note:))
            }
        }
    }
}

SEL 在哪里實(shí)現(xiàn)

關(guān)于上面的event.selector是如何實(shí)現(xiàn)調(diào)用的。實(shí)際上是通過閉包的形式調(diào)用,作者在這里實(shí)現(xiàn)了每一個(gè)的監(jiān)聽響應(yīng)方法,然后在該方法里去調(diào)用閉包來做。具體閉包的實(shí)現(xiàn)是使用者來實(shí)現(xiàn)的,然后通過key為event的dict來獲取閉包。
舉例代碼:

internal func keyboardWillShow(note: Notification) {
    if let callback = callbacks[.willShow] {
        callback(keyboardOptions(fromNotificationDictionary: note.userInfo))
    }
}

KeyboardOptions

這里主要是一些與鍵盤有關(guān)的信息。


總結(jié)

簡單總結(jié)下該優(yōu)雅的邏輯:

  1. let keyboard = Typist.shared
  2. 調(diào)用func on(event: KeyboardEvent, do callback: TypistCallback?),
    • 該方法中僅僅做了一件事:callbacks[event] = callback
    • 顯然這個(gè)閉包是我們?nèi)?shí)現(xiàn)的。
  3. 調(diào)用start()方法
    • 遍歷了callbacks, 依次實(shí)現(xiàn)了addObserve方法,主要兩個(gè)參數(shù)是selector: event.selector, name: event.notification
    • 其中event.notification event 和keyboard NSNotification.Name 的映射, event.selector中去調(diào)用了callbacks中的閉包,也就是我們的實(shí)現(xiàn)事件。
  4. 可以調(diào)用stop()方法移除通知。
    • 本質(zhì)上是調(diào)用了removeObserver方法.

參考

Typist GitHub Repo
原文地址:https://vsccw.com/2017/02/26/for-typist/

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

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

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