LoginSignup
41
39

More than 5 years have passed since last update.

【プロトコル指向】キーボードを簡単に閉じることができるProtocol

Last updated at Posted at 2016-06-22

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()
    }
}

プロトコル指向で、見た目が非常に美しくなります:smile:

41
39
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
39