iOSアプリを開発していてキーボードが邪魔になることってよくありますよね。
そんな時はUITapGestureRecognizer
を付け、画面をタップするとview.endEditing(true)
を呼んでキーボードを閉じるという処理を書くと思います。
ただ、キーボードを使用する画面ごとにその処理を記述するのは気持ち悪いですよね。
そこで、Protocolを適合させ、メソッドを一つ呼ぶだけで上記の処理を組み込むことができるようにしてみました。
KeyboardCloseable
KeyboardCloseable.swift
import UIKit
private var keyboardKey: Void?
private var gestureKey: Void?
public protocol KeyboardCloseable: class {
}
extension KeyboardCloseable where Self: UIViewController {
public func addKeyboardCloseGesture() {
let keyboardAction = KeyboardAction(view: view) { [weak self] in
self?.view.endEditing(true)
}
let gestureForKeyboard = UITapGestureRecognizer(target: keyboardAction, action: #selector(keyboardAction.action))
gestureForKeyboard.delegate = keyboardAction
view.addGestureRecognizer(gestureForKeyboard)
// KeyboardActionがGCで回収されないようにする
objc_setAssociatedObject(self, &keyboardKey, keyboardAction, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_setAssociatedObject(self, &gestureKey, gestureForKeyboard, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
public func removeKeyboardCloseGesture() {
if let gesture = objc_getAssociatedObject(self, &gestureKey) as? UITapGestureRecognizer {
view.removeGestureRecognizer(gesture)
}
objc_removeAssociatedObjects(self)
}
}
private final class KeyboardAction: NSObject, UIGestureRecognizerDelegate {
private let _action: () -> ()
private weak var view: UIView?
init(view: UIView, action: () -> ()) {
self.view = view
_action = action
}
@objc private func action() {
_action()
}
@objc private func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
// キーボード表示中だけGestureを有効に
return view?.findFirstResponder() != nil
}
}
private extension UIView {
private func findFirstResponder() -> UIView? {
guard !isFirstResponder() else { return self }
for view in subviews {
guard !view.isFirstResponder() else { return view }
if let responder = view.findFirstResponder() {
return responder
}
}
return nil
}
}
使い方
// KeyboardCloseableに適合
class ViewController: UIViewController, KeyboardCloseable {
override func viewDidLoad() {
super.viewDidLoad()
// これを呼ぶだけ
addKeyboardCloseGesture()
}
}
プロトコル指向で、見た目が非常に美しくなります