Pure Swift 之 Target Action 處理

這幾天在寫 Swift 4 的時候,一如往常的在處理 Keyboard 的 show/hide 問題。

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
func keyboardWillShow(_ notification: Notification) {
//實作 keyboard will show
}

結果偉大的 Xcode 9 跳出了警告

Argument of ‘#selector’ refers to instance method ‘keyboardWillShow’ that is not exposed to Objective-C
Add ‘@objc’ to expose this instance method to Objective-C

這段英文的意思我就不翻譯了,但總結是說要在 func 前面加 @objc 如下

@objc func keyboardWillShow(_ notification: Notification) {
//實作 keyboard will show
}

我才驚覺,Target Action 是 Objective C 的 Design Pattern。(早該發現了,之前都混什麼吃的)

在 Swift 的 Virtual Table 設計的像是 Array 一樣,所以在取 func 的時候是使用 index 來取。

在 Objective C 的 Virtual Table 則是像 Dictionary 一樣,取 func 是把 selector 的名稱當成 key 去查表。

也就是說在不考慮其他條件下,Swift 取 func 的速度是比 Objective C 還快的。

我想要 Clean Swift 的寫法,想說 Swift 是否有其他的 Pattern 來做到一樣的功能。

詢問了 zonble ,得知純 Swift 沒有 Selector,要用 Selector 就一定會透過 @objc 來跟 Objective C 做 Bridge。

慶幸的是 AddObserver 有出 Block 版本( Swift 稱為 Closure)

將程式碼改為以下

NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillShow, object: nil, queue: OperationQueue.main) { (notification) in
self.keyboardWillShow(notification)
}
func keyboardWillShow(_ notification: Notification) {
//實作 keyboard will show
}

因為這是個跟畫面有關係的行為,所以要到 Main queue 處理。

常用的 Targer Action 還有 NSTimer (Swift 稱 Timer),我之前也有用Accelerometer(加速儀)也可以使用 Block,這樣可以在 Swift 裡面減少使用 objc 這個歷史的包袱。

不過目前仍然有遇到沒有辦法透過 block 來處理的情況,如 Gesture。寫法還是只能像以下那樣。

let tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(didTap))
@objc func didTap() {
//實作 did tap
}

如果有人知道 Gesture 怎麼寫可以讓 Swift 更 Clean 的話,還請務必指點一下。

註:我今天才知道原來有 Clean Swift 這種說法,好像就是盡量少跟 Objc 扯上關係的寫法。

註2:keyboard show/hide 的實作這邊是省略的。

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.