40
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-01-22

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

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

40
38
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?