この記事では、基本から実装までのビューをクリックして、キーボードを非表示にする方法を1行で、またはコードなしで分析します。
基本
次のケースは非常に一般的です。背景をクリックして、キーボードを非表示にします。
基本的な解決策は、 UITextField
へのリンクを作成し、テキストフィールドから選択を削除するメソッドでUITapGestureRecognizer
を作成し、次のようにすることです。
この記事ではSwift 3を使用していますが、他のバージョンやObjective-Cでも実装できます
class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! override func viewDidLoad() { super.viewDidLoad() let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture)) view.addGestureRecognizer(tapGesture) } func tapGesture() { textField.resignFirstResponder() } }
このコードの問題:
viewDidLoad
ダーティで読めない- コントローラ内の多くのコード
- 再利用不可
読みやすくする
最初の問題を解決するには、別の関数でジェスチャーを作成および追加するためのコードを取り出します。
class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! override func viewDidLoad() { super.viewDidLoad() addTapGestureToHideKeyboard() } func addTapGestureToHideKeyboard() { let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture)) view.addGestureRecognizer(tapGesture) } func tapGesture() { textField.resignFirstResponder() } }
コードはさらに多くなりましたが、よりクリーンで、論理的で、見た目も快適になりました。
コード削減
2番目の問題を解決するために、 UIView
は次のメソッドがあります。
func endEditing(_ force: Bool) -> Bool
彼は、ビュー自体またはそのサブビューから選択を削除するだけです。 彼のおかげで、コードを大幅に簡素化できます。
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() addTapGestureToHideKeyboard() } func addTapGestureToHideKeyboard() { let tapGesture = UITapGestureRecognizer(target: view, action: #selector(view.endEditing)) view.addGestureRecognizer(tapGesture) } }
手順に従う場合は、必ずIBからtextFieldプロパティを削除してください。
また、 target
をself
からview
変更しview
。
コードは目を楽しませ始めました! ただし、これを各コントローラーにコピーする必要があります。
コピーソリューション
再利用のために、 extension
コントローラーを追加する方法を紹介します。
extension UIViewController { func addTapGestureToHideKeyboard() { let tapGesture = UITapGestureRecognizer(target: view, action: #selector(view.endEditing)) view.addGestureRecognizer(tapGesture) } }
そして、コントローラーのコードは次のようになります。
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() addTapGestureToHideKeyboard() } }
きれいな1行のコードで、再利用可能です! パーフェクト!
マルチビュー
上記のソリューションは非常に優れていますが、欠点が1つあります。特定のビューにジェスチャーを追加することはできません。
このケースを解決するには、 UIView
拡張機能を使用します。
extension UIView { func addTapGestureToHideKeyboard() { let tapGesture = UITapGestureRecognizer(target: self, action: #selector(endEditing)) addGestureRecognizer(tapGesture) } }
したがって、コントローラーコードは次のようになります。
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.addTapGestureToHideKeyboard() } }
ここで別の問題が発生します。この拡張機能は、コントローラービューの問題のみを解決します。 someViewをビューに追加し、その上にジェスチャーを配置すると、機能しなくなります。 これは、 endEditing
メソッドがアクティブビューを含むビューまたはそれ自体のビューに対してのみ機能し、テキストフィールドがその中にない可能性が高いためです。 この問題を解決します。
なぜなら コントローラーのビューには必ずアクティブビューが含まれ、追加したビューは常にその階層内にあります。その後、 superview
を介してsuperview
コントローラーに到達し、そのendEditing
を呼び出します。
UIView拡張機能を通じてコントローラーのビューを取得します。
var topSuperview: UIView? { var view = superview while view?.superview != nil { view = view!.superview } return view }
セレクタを次のように変更して、すぐに言います
#selector(topSuperview?.endEditing)
それでも動作しません。 上記の構成を呼び出すメソッドを追加する必要があります。
func dismissKeyboard() { topSuperview?.endEditing(true) }
次に、セレクターを次のように置き換えます。
#selector(dismissKeyboard)
したがって、UIViewの拡張機能は次のようになります。
extension UIView { func addTapGestureToHideKeyboard() { let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) addGestureRecognizer(tapGesture) } var topSuperview: UIView? { var view = superview while view?.superview != nil { view = view!.superview } return view } func dismissKeyboard() { topSuperview?.endEditing(true) } }
次に、任意のビューにaddTapGestureToHideKeyboard()
を使用して、キーボードを非表示にします。
KeyboardHideManager

上記のソリューションを長い間使用していましたが、1行でもビューインストール機能を汚染していることに気付き始めました。 また、(まれですが、それでも起こります) viewDidLoad
唯一のメソッドである場合、あまり美しくありません:
override func viewDidLoad() { super.viewDidLoad() addTapGestureToHideKeyboard() }
スペースと合わせて5行かかるため、コントローラーの純度に大きく影響します! コードなしでこれをすべて実行することを考えていたので、コントローラーに1行余分に追加する必要はありません。 Objectを使用してIBに追加できるクラスを作成しました

そして、 @IBOutlet
必要な@IBOutlet
をバインドし@IBOutlet
。

このクラスの実装は最も簡単です-上記で作成した関数を使用して、各ビューにジェスチャーを追加します。
final public class KeyboardHideManager: NSObject { @IBOutlet internal var targets: [UIView]! { didSet { for target in targets { addGesture(to: target) } } } internal func addGesture(to target: UIView) { let gesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) target.addGestureRecognizer(gesture) } @objc internal func dismissKeyboard() { targets.first?.topSuperview?.endEditing(true) } } extension UIView { internal var topSuperview: UIView? { var view = superview while view?.superview != nil { view = view!.superview } return view } }
このクラスを使用するには、3つの簡単な手順が必要です。
- 1) オブジェクトをコントローラーにドラッグします

- 2) KeyboardHideManagerをこのオブジェクトのクラスとして設定します

- 3)目的のビューをターゲットプロパティに接続します

はい、このコントローラーにはこの行がないため、単一行(または定義されたビューが複数ある場合は複数行)を書き込むよりも多くのアクションがあります。
誰かがコードに線を書く方が良いと言うかもしれません、それはより明確で、一方で彼らは正しいでしょう。 コントローラーから可能な限りすべてを削除して、それを促進することに賛成です。
このクラスをCocoaPods経由で接続するか、単にプロジェクトにコピーできます。
ライブラリ自体とその接続に関する完全なReadMeを使用して、 KeyboardHideManagerのソースコードにリンクします。
まとめ
人気のあるケースの実装を分解し、そのソリューションのいくつかを1行でコードなしで調べました。 好きな方法を使用してください。