post Image
【Swift】キーボードにUIPickerViewを表示する

1.はじめに

 UITextViewUITextField の入力時のキーボードに UIPickerView を表示する記事はよく見かけますが、UIButton 契機で表示する方法はあまりなかったようなので記事にしました。
 とはいえ、UITextViewUITextField とやることは特に変わりません。UIButton の inputView をオーバーライドしてUIPickerView を返すだけです。
 また、UIResponder を継承したクラス(UIView 系)は全て、inputView というプロパティを持ちます。ここに任意のview を設定することで、システムキーボードの代わりに表示させることができます。

2.環境

Xcode Version 9.2
Swift Version 4.0.3

3.コード① PickerViewKeyboard の本体

 UIButton を継承したPickerViewKeyboard を定義します。
 ポイントは以下の通りです。

  • canBecomeFirstResponder をオーバーライドしてtrue を返すことでキーボードを表示できるようにする
  • dataUIPickerView のデータソースを定義する
  • 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メソッド

 ピッカービューの各種定義を行います。
 ポイントは以下の通りです。

  • UIPickerViewDelegateUIPickerViewDataSource の適合を忘れずに
  • 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 でデータソースを返す
didCanceldidDone でキーボードのアクセサリービューのボタンをタップした際の処理を定義する


protocol PickerViewKeyboardDelegate {
    func titlesOfPickerViewKeyboard(sender: PickerViewKeyboard) -> Array<String>
    func initSelectedRow(sender: PickerViewKeyboard) -> Int
    func didCancel(sender: PickerViewKeyboard)
    func didDone(sender: PickerViewKeyboard, selectedData: String)
}

6.使い方

storyboardUIButton をビューに配置します。カスタムクラスにPickerViewKeyboard を設定します(スクリーンショット参照)。
 ViewController にアウトレット接続し、viewDidLoad などでPickerViewKeyboarddelegateself を設定します。
 下記のViewController ではキーボードのピッカービューから取得した値をUILabel に設定しています。

スクリーンショット.png


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に表示されます。

スクリーンショット.png

スクリーンショット.png

間違っている点や改善点などありましたら、ぜひご指摘をお願いします。


『 Swift 』Article List