IQKeyboardManagerで親Viewが異なるUITextFieldを兄弟とみなす方法
課題
Qiitaで紹介されていて使ってみたIQKeyboardManager、すごい便利ですね。
IQKeyboardManagerは、デフォルトでは画像のように別の親Viewに属しているUITextFieldなどを兄弟と見なしてくれません。(つまりToolbarの↑↓でUITextFieldを移動できません。)
原因
IQKeyboardManagerのソースコードを見てみます。
UITextField兄弟間をToolbarの↑↓で移動するロジックは、兄弟をresponderViews()
という関数で探索していることが分かります。
@objc public var canGoNext: Bool { //Getting all responder view's. if let textFields = responderViews() { if let textFieldRetain = _textFieldView { //Getting index of current textField. if let index = textFields.index(of: textFieldRetain) { //If it is not first textField. then it's previous object canBecomeFirstResponder. if index < textFields.count-1 { return true } } } } return false }
responderViews()
はまず、全てのsuperViewからtoolbarPreviousNextAllowedClasses
の配列に含まれるクラスと一致するものを探索します。
もし一致するsuperViewが見つかった場合は、そのViewの全ての子Viewから兄弟を探索し、Viewの配列として返却します。
もし一致するsuperViewが1つも見つからなかった場合は、対象のUITextFieldと同じ階層にある兄弟だけを探索して返却します。
/** Get all UITextField/UITextView siblings of textFieldView. */ private func responderViews()-> [UIView]? { var superConsideredView : UIView? //If find any consider responderView in it's upper hierarchy then will get deepResponderView. for disabledClass in toolbarPreviousNextAllowedClasses { superConsideredView = _textFieldView?.superviewOfClassType(disabledClass) if superConsideredView != nil { break } } //If there is a superConsideredView in view's hierarchy, then fetching all it's subview that responds. No sorting for superConsideredView, it's by subView position. (Enhancement ID: #22) if let view = superConsideredView { return view.deepResponderViews() } else { //Otherwise fetching all the siblings //...
つまり、兄弟にしたいUITextFieldを全て、toolbarPreviousNextAllowedClasses
に含まれるクラスの子Viewにすれば、兄弟として判別してくれるというロジックのようです。
対策
まず、親View用のカスタムViewクラスを定義します。
今回はTextFieldsContainerView
という名前でクラスを定義しました。
import UIKit /// Class for letting IQKeyboardManager treat all text fields/views whose parent is this container view /// as a sibling so that it enables to handle all on its toolbar. /// We don't need this class all the time but useful when we have text fields/views that have different parent. /// See also: https://github.com/hackiftekhar/IQKeyboardManager/blob/master/IQKeyboardManagerSwift/IQKeyboardManager.swift#L1716-L1728 class TextFieldsContainerView: UIView { }
次に、IQKeyboardManagerのtoolbarPreviousNextAllowedClasses
に先ほど定義したTextFieldsContainerViewクラスを追加します。
IQKeyboardManager.shared.toolbarPreviousNextAllowedClasses.append(TextFieldsContainerView.self)
最後に、TextFieldsContainerViewクラスをstoryboard/xibやコードで各UITextFieldの親Viewクラスになるように実装します。