1.はじめに
UITextView
や UITextField
の入力時のキーボードに UIPickerView
を表示する記事はよく見かけますが、UIButton
契機で表示する方法はあまりなかったようなので記事にしました。
とはいえ、UITextView
や UITextField
とやることは特に変わりません。UIButton
の inputView
をオーバーライドしてUIPickerView
を返すだけです。
また、UIResponder
を継承したクラス(UIView
系)は全て、inputView
というプロパティを持ちます。ここに任意のview
を設定することで、システムキーボードの代わりに表示させることができます。
2.環境
Xcode Version 9.2
Swift Version 4.0.3
3.コード① PickerViewKeyboard
の本体
UIButton
を継承したPickerViewKeyboard
を定義します。
ポイントは以下の通りです。
-
canBecomeFirstResponder
をオーバーライドしてtrue
を返すことでキーボードを表示できるようにする -
data
にUIPickerView
のデータソースを定義する -
init()
でaddTarget
する。当ボタンがタップされた際にdidTouchUpInside
が呼ばれるようにする。didTouchUpInside
でキーボードを表示している -
inputView
をオーバーライドし、UIPickerView
を返すことで、キーボードにUIPickerView
を表示する -
inputAccessoryView
をオーバーライドし、各種ボタンを定義したUIToolBar
を返す。ここで返したview
がキーボード上部のcancel
ボタンやdone
ボタンになる -
cancelPicker()
、donePicker()
で呼ぶdelegateメソッドは後ほど
class PickerViewKeyboard: UIButton {
var delegate: PickerViewKeyboardDelegate!
var pickerView: UIPickerView!
override var canBecomeFirstResponder: Bool {
return true
}
// ピッカーに表示させるデータ
var data: Array<String> {
return delegate.titlesOfPickerViewKeyboard(sender: self)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.addTarget(self, action: #selector(didTouchUpInside(_:)), for: .touchUpInside)
}
@objc func didTouchUpInside(_ sender: UIButton) {
becomeFirstResponder()
}
override var inputView: UIView? {
pickerView = UIPickerView()
pickerView.delegate = self
let row = delegate.initSelectedRow(sender: self)
pickerView.selectRow(row, inComponent: 0, animated: true)
return pickerView
}
override var inputAccessoryView: UIView? {
let toolbar = UIToolbar()
toolbar.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: 44)
let space = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: self, action: nil)
space.width = 12
let cancelItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(PickerViewKeyboard.cancelPicker))
let flexSpaceItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
let doneButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(PickerViewKeyboard.donePicker))
let toolbarItems = [space, cancelItem, flexSpaceItem, doneButtonItem, space]
toolbar.setItems(toolbarItems, animated: true)
return toolbar
}
@objc func cancelPicker() {
delegate.didCancel(sender: self)
}
@objc func donePicker() {
delegate.didDone(sender: self, selectedData: data[pickerView.selectedRow(inComponent: 0)])
}
}
4.コード② UIPickerView
のdelegateメソッド
ピッカービューの各種定義を行います。
ポイントは以下の通りです。
-
UIPickerViewDelegate
、UIPickerViewDataSource
の適合を忘れずに -
pickerView(_, didSelectRow, inComponent)
でピッカービューの値が変わった時の処理を実装する。ここでは選択したインデックスを退避
extension PickerViewKeyboard: UIPickerViewDelegate, UIPickerViewDataSource {
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return data.count
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return data[row]
}
}
5.コード③ PickerViewKeyboardDelegate
の定義
データソースの取得やキーボードを閉じる際に呼ばれるデリゲートメソッドを定義します。
ポイントは以下の通りです。
-
titlesOfPickerViewKeyboard
でデータソースを返す -
didCancel
、didDone
でキーボードのアクセサリービューのボタンをタップした際の処理を定義する
protocol PickerViewKeyboardDelegate {
func titlesOfPickerViewKeyboard(sender: PickerViewKeyboard) -> Array<String>
func initSelectedRow(sender: PickerViewKeyboard) -> Int
func didCancel(sender: PickerViewKeyboard)
func didDone(sender: PickerViewKeyboard, selectedData: String)
}
6.使い方
storyboard
でUIButton
をビューに配置します。カスタムクラスにPickerViewKeyboard
を設定します(スクリーンショット参照)。
ViewController
にアウトレット接続し、viewDidLoad
などでPickerViewKeyboard
のdelegate
にself
を設定します。
下記のViewController
ではキーボードのピッカービューから取得した値をUILabel
に設定しています。
class ViewController: UIViewController {
@IBOutlet weak var pickerKeyboardButton: PickerViewKeyboard!
@IBOutlet weak var resultLabel: UILabel!
let dataSource = ["いち", "に", "さん", "よん", "ご", "ろく", "なな"]
override func viewDidLoad() {
super.viewDidLoad()
pickerKeyboardButton.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
extension ViewController: PickerViewKeyboardDelegate {
func titlesOfPickerViewKeyboard(sender: PickerViewKeyboard) -> Array<String> {
return dataSource
}
func initSelectedRow(sender: PickerViewKeyboard) -> Int {
return 3
}
func didDone(sender: PickerViewKeyboard, selectedData: String) {
resultLabel.text = selectedData
}
func didCancel(sender: PickerViewKeyboard) {
print("canceled")
}
}
ボタンをタップすると画面下部のキーボードのエリアにピッカービューが表示されます。
Doneボタンをタップすると選択中の文字列がLabelに表示されます。
間違っている点や改善点などありましたら、ぜひご指摘をお願いします。