こんにちは。
入社して一ヶ月が経過したiOSエンジニアの弘田です。
今回はUIPickerViewをキーボードの様に表示する方法を解説します。
なぜそんなことをするの?
昔のiPhoneでしたら画面の中心などにUIPickerViewを表示しても画面サイズが小さかったので片手で操作できていましたが、最近は大画面化が進み片手での操作が難しくなってきました。
操作性を損なわずにUIPickerViewを使用してもらう為にも今回の方法が役にたつと思います。
Human Interface GuidelinesでもPickerついて触れているページがありこの様な記載があります。
Avoid switching screens to show a picker. A picker works well when displayed in context, below or in close proximity to the field being edited.
翻訳
ピッカーを表示するように画面を切り替えることは避けてください。ピッカーは、編集中のフィールドの下、または近くにコンテキストで表示されたときにうまく機能します。
今回目指すもの
※シミューレーターなのでキーボードを閉じるアニメーションが少しおかしいです
実装
1. UIControlを継承したclassを作る
class pickerKeyboard: UIControl { }
2.イニシャライザを作成し、自身がタップされた時にinputViewを出す処理を作る
class pickerKeyboard: UIControl { required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) addTarget(self, action: #selector(tappedPickerKeyboard(_:)), for: .touchDown) } @objc private func tappedPickerKeyboard(_ sender: PickerKeyboard) { self.becomeFirstResponder() } }
3.canBecomeFirstResponderをtrueで返して自身をFirstResponderにする
canBecomeFirstResponderのデフォルトはfalseになっていて、
trueを返さないと後述のinputViewで指定したViewが表示されません。
class pickerKeyboard: UIControl { //~~~省略~~~ override var canBecomeFirstResponder: Bool { return true } }
4.FirstResponderになった上でinputViewをoverrideする
ここでは表示したいViewを返します。
今回はUIPickerViewをaddSubviewしたUIViewを返します。
UIViewを返す理由はSafeAreaに対応するためです。
inputViewについて(Apple公式)
class pickerKeyboard: UIControl { //~~~省略~~~ override var inputView: UIView? { let pickerView: UIPickerView = UIPickerView() pickerView.delegate = self pickerView.dataSource = self pickerView.backgroundColor = UIColor.white pickerView.autoresizingMask = [.flexibleHeight] // SafeArea対応をする為にUIViewを挟む let view = UIView() view.backgroundColor = .white view.autoresizingMask = [.flexibleHeight] view.addSubview(pickerView) pickerView.translatesAutoresizingMaskIntoConstraints = false pickerView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true pickerView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true pickerView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor).isActive = true return view } }
5.inputAccessoryViewをoverrideしてUIPickerViewを閉じるボタンを作る
class pickerKeyboard: UIControl { //~~~省略~~~ override var inputAccessoryView: UIView? { let view = UIVisualEffectView(effect: UIBlurEffect(style: .extraLight)) view.frame = CGRect(x: 0, y: 0, width: frame.width, height: 44) let closeButton = UIButton(type: .custom) closeButton.setTitle("閉じる", for: .normal) closeButton.sizeToFit() closeButton.addTarget(self, action: #selector(tappedCloseButton(_:)), for: .touchUpInside) closeButton.setTitleColor(UIColor(red: 0, green: 122/255, blue: 1, alpha: 1.0), for: .normal) view.contentView.addSubview(closeButton) closeButton.translatesAutoresizingMaskIntoConstraints = false closeButton.widthAnchor.constraint(equalToConstant: closeButton.frame.size.width).isActive = true closeButton.heightAnchor.constraint(equalToConstant: closeButton.frame.size.height).isActive = true closeButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 5).isActive = true closeButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true return view } @objc private func tappedCloseButton(_ sender: UIButton) { resignFirstResponder() } }
6.通常のUIPickerViewと同様にUIPickerViewDelegateとUIPickerViewDataSourceを継承してデータを表示
class pickerKeyboard: UIControl { let array:[String] = ["A","B","C","D","E"] //~~~省略~~~ } extension PickerKeyboard: UIPickerViewDelegate, UIPickerViewDataSource { func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return array.count } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return array[row] } func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { // delegateなどでViewControllerに選択された情報を渡す } }
7.StoryboradやXibでUIViewのCustomClassとして設定する
まとめ
手順4のUIPickerViewを作った時にpickerView.backgroundColor = UIColor.white
としているのでわかり難いですが、
別の色に変更するとSafeArea対応できていることが確認できます。
記事で紹介したコードはGithubで公開しています。
https://github.com/srknra/PickerKeyboard
今回はUIPickerViewをキーボードの様に表示してUXを低下させない様な工夫でしたが、
Studyplusのアプリでは他にもユーザーの事を考えて様々な工夫をしてるので今後もブログで紹介していこうと思います。