前言
在iOS想要監(jiān)聽鍵盤的話是通過注冊通知、接收鍵盤發(fā)來的通知實現(xiàn)的爽航,雖然步驟不是很多横浑,不過由于鍵盤的狀態(tài)很多(有6種),如果需求要監(jiān)聽所有的狀態(tài)腻暮,想想你要把相同的代碼寫6次彤守,是有多煩人毯侦。而且適配的系統(tǒng)在iOS 9以下還要把一個個通知移除。這是有多不簡潔...不過自從我遇到了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ào)用起宽。并沒有Rx那么高大上哈洲胖。
如何你想要移除鍵盤監(jiān)聽,那么直接調(diào)用keyboard.clear()
即可坯沪。
不過這里有點需要注意下绿映,當有多個不同的對象都要監(jiān)聽鍵盤時,不要使用該單例腐晾。也就說這種情況下就需要你自己去創(chuàng)建Typist對象了叉弦,不要使用Typist.shared
這個會導致你其中某些對象接受不到通知。
從鍵盤監(jiān)聽說起
在不使用Typist的情況下藻糖,我們是怎么做鍵盤監(jiān)聽的呢淹冰?
具體步驟如圖所示。不過在如果你的應(yīng)用支持iOS 9+以上颖御,那么就可以不用去移除通知榄棵,這個在Apple文檔里也有說明。
可能只是一張圖你看不出來有多煩人潘拱,那么我用代碼演示下是這樣的疹鳄。
而且這僅僅只是監(jiān)聽了鍵盤的實現(xiàn)方法,就已經(jīng)需要這么多代碼了芦岂。那么我們要是遇到需求要監(jiān)聽所有的鍵盤通知呢瘪弓?那么上面代碼就成了這樣:
一堆樣式相同的模板代碼,有人會說禽最,我需要一個函數(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)建一個Typist對象呛占;#2 處監(jiān)聽了鍵盤顯示結(jié)束,閉包回調(diào)里的options包含了一些鍵盤信息懦趋,也就是NotificationCenter
里面的info
信息晾虑;#3 處監(jiān)聽了鍵盤的隱藏結(jié)束,閉包回調(diào)同上;#4 在這里開始監(jiān)聽帜篇,相當與我們使用的addobserver: 方法糙捺。
具體的邏輯如下圖所示:
那么它內(nèi)部究竟是怎么做的呢?
public func on(event: KeyboardEvent, do callback: TypistCallback?) -> Self
方法
這個方式的實現(xiàn)很簡單笙隙,僅僅是將callbac回調(diào)放入一個dict里面洪灯,dict的key是event,然后返回自己竟痰。
public func on(event: KeyboardEvent, do callback: TypistCallback?) -> Self {
callbacks[event] = callback
return self
}
KeyboardEvent
作者在這里定義了一個枚舉來封裝了各個不同的鍵盤監(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 屬性
這兩個KeyboardEvent擴展下的私有屬性分別返回上面提到的 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 在哪里實現(xiàn)
關(guān)于上面的event.selector
是如何實現(xiàn)調(diào)用的柠并。實際上是通過閉包的形式調(diào)用,作者在這里實現(xiàn)了每一個的監(jiān)聽響應(yīng)方法富拗,然后在該方法里去調(diào)用閉包來做臼予。具體閉包的實現(xiàn)是使用者來實現(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)雅的邏輯:
let keyboard = Typist.shared
- 調(diào)用
func on(event: KeyboardEvent, do callback: TypistCallback?)
,- 該方法中僅僅做了一件事:
callbacks[event] = callback
- 顯然這個閉包是我們?nèi)崿F(xiàn)的。
- 該方法中僅僅做了一件事:
- 調(diào)用
start()
方法- 遍歷了
callbacks
, 依次實現(xiàn)了addObserve方法创千,主要兩個參數(shù)是selector: event.selector, name: event.notification
- 其中
event.notification
event 和keyboard NSNotification.Name 的映射,event.selector
中去調(diào)用了callbacks中的閉包缰雇,也就是我們的實現(xiàn)事件。
- 遍歷了
- 可以調(diào)用stop()方法移除通知追驴。
- 本質(zhì)上是調(diào)用了
removeObserver
方法.
- 本質(zhì)上是調(diào)用了
參考
Typist GitHub Repo
原文地址:https://vsccw.com/2017/02/26/for-typist/