5
6

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-11-01

##はじめに
ジーズアカデミー卒業生のyukingと申します。今回SwiftにおけるPickerViewの実装方法を前回投稿したものの改善版として投稿させていただきます。

##躓いた点
1つのViewControllerに2つ以上のUIPickerViewを導入しようとした時に、ふと「あれ?複数のUIPIckerViewを表示って難しくね?考えたらdelegateで複数個なんて返せない.....」と思ったことがきっかけです。

Sample.swift
class SampleViewController: UIViewController {
    @IBOutlet weak var HogeTextField: UITextField!
    @IBOutlet weak var HugaTextField: UITextField!

    var pickerForHoge = ["hoge1", "hoge2", "hoge3", "hoge4", "hoge5"]
    var pickerForHuga = ["huga1", "huga2", "huga3", "huga4", "huga5"]

    @objc func onDoneButtonTappedForHoge(sender: UIBarButtonItem) {
        if HogeTextField.isFirstResponder {
            HogeTextField.resignFirstResponder()
        }
    }
    
    @objc func onDoneButtonTappedForHuga(sender: UIBarButtonItem) {
        if HugaTextField.isFirstResponder {
            HugaTextField.resignFirstResponder()
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        setupUIForHoge()
        setupUIForHuga()
    }
}

private extension SampleViewController {
    //ツールバー表示
    var accessoryToolbarForHoge: UIToolbar {
        get {
            let toolbarFrame = CGRect(x: 0, y: 0,
                                      width: view.frame.width, height: 44)
            let accessoryToolbar = UIToolbar(frame: toolbarFrame)
            let doneButton = UIBarButtonItem(barButtonSystemItem: .done,
                                             target: self,
                                             action: #selector(onDoneButtonTappedForHoge(sender:)))
            let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
                                                target: nil,
                                                action: nil)
            accessoryToolbar.items = [flexibleSpace, doneButton]
            accessoryToolbar.barTintColor = UIColor.white
            return accessoryToolbar
        }
    }
    
    var accessoryToolbarForHuga: UIToolbar {
        get {
            let toolbarFrame = CGRect(x: 0, y: 0,
                                      width: view.frame.width, height: 44)
            let accessoryToolbar = UIToolbar(frame: toolbarFrame)
            let doneButton = UIBarButtonItem(barButtonSystemItem: .done,
                                             target: self,
                                             action: #selector(onDoneButtonTappedForHuga(sender:)))
            let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
                                                target: nil,
                                                action: nil)
            accessoryToolbar.items = [flexibleSpace, doneButton]
            accessoryToolbar.barTintColor = UIColor.white
            return accessoryToolbar
        }
    }

    func setupUIForHoge() {
        HogeTextField.inputView = getPickerView()
        HogeTextField.inputAccessoryView = accessoryToolbarForHoge
        HogeTextField.text = pickerForHoge[0]
    }

    func setupUIForHuga() {
        HugaTextField.inputView = getPickerView()
        HugaTextField.inputAccessoryView = accessoryToolbarForHuga
        HugaTextField.text = pickerForHuga[0]
    }

    func getPickerView() -> UIPickerView {
        var pickerView = UIPickerView()
        //ここで疑問
        //2つのpickerViewを用意したから、delegateもその2つ分用意しなくてはいけないのでは?どうすればいいのか?
        pickerView.dataSource = self
        pickerView.delegate = self
        pickerView.backgroundColor = UIColor.white
        return pickerView
    }
}

そこから試行錯誤、質問を繰り返し理解できたので、その方法を紹介していきたいと思います。
UIPickerViewが分からないという方は、https://qiita.com/hijion/items/a93971d8bd945e771be7こういった記事を参考にしてみてください。

##解決法
enum(列挙体)を使うことで解決できました。列挙体が分からない方は、https://www.sejuku.net/blog/35711こちらの記事を参考にしてみてください。
それでは具体的にコードを記していきます。

Sample.swift
class SampleViewController: UIViewController {
    //ここでpickerの数分のenumを定義。今回は2つなので、2つ用意します。
    enum PickerType {
        case Hoge
        case Huga
    }
    @IBOutlet weak var HogeTextField: UITextField!
    @IBOutlet weak var HugaTextField: UITextField!

    var pickerForHoge = ["hoge1", "hoge2", "hoge3", "hoge4", "hoge5"]
    var pickerForHuga = ["huga1", "huga2", "huga3", "huga4", "huga5"]

    @objc func onDoneButtonTappedForHoge(sender: UIBarButtonItem) {
        if HogeTextField.isFirstResponder {
            HogeTextField.resignFirstResponder()
        }
    }

    @objc func onDoneButtonTappedForHuga(sender: UIBarButtonItem) {
        if HugaTextField.isFirstResponder {
            HugaTextField.resignFirstResponder()
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        setupUIForHoge()
        setupUIForHuga()
    }
}

private extension SampleViewController {
    //ツールバー表示
    var accessoryToolbarForHoge: UIToolbar {
        get {
            let toolbarFrame = CGRect(x: 0, y: 0,
                                      width: view.frame.width, height: 44)
            let accessoryToolbar = UIToolbar(frame: toolbarFrame)
            let doneButton = UIBarButtonItem(barButtonSystemItem: .done,
                                             target: self,
                                             action: #selector(onDoneButtonTappedForHoge(sender:)))
            let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
                                                target: nil,
                                                action: nil)
            accessoryToolbar.items = [flexibleSpace, doneButton]
            accessoryToolbar.barTintColor = UIColor.white
            return accessoryToolbar
        }
    }

    var accessoryToolbarForHuga: UIToolbar {
        get {
            let toolbarFrame = CGRect(x: 0, y: 0,
                                      width: view.frame.width, height: 44)
            let accessoryToolbar = UIToolbar(frame: toolbarFrame)
            let doneButton = UIBarButtonItem(barButtonSystemItem: .done,
                                             target: self,
                                             action: #selector(onDoneButtonTappedForHuga(sender:)))
            let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
                                                target: nil,
                                                action: nil)
            accessoryToolbar.items = [flexibleSpace, doneButton]
            accessoryToolbar.barTintColor = UIColor.white
            return accessoryToolbar
        }
    }

    func setupUIForHoge() {
        //UIPickerViewのインスタンスを作る時に、引数にHogeを渡す
        HogeTextField.inputView = getPickerView(type: .Hoge)
        HogeTextField.inputAccessoryView = accessoryToolbarForHoge
        HogeTextField.text = pickerForHoge[0]
    }

    func setupUIForHuga() {
        //UIPickerViewのインスタンスを作る時に、引数にHugaを渡す
        HugaTextField.inputView = getPickerView(type: .Huga)
        HugaTextField.inputAccessoryView = accessoryToolbarForHuga
        HugaTextField.text = pickerForHuga[0]
    }
    //引数にenumを入れる
    func getPickerView(type: PickerType) -> UIPickerView {
        //ここで大元のreturnするためのpickerのインスタンス作成。
        //ここでインスタンスを作成しないと「Ambiguous reference to member 'pickerView(_:numberOfRowsInComponent:)'」というエラーがでる。
        var pickerView = UIPickerView() 
        //タイプ分け。enumで定義した数だけswitch文でcase分けすることで、getPickerView(type: PickerType)関数を使用した時に、サブクラスを作った数だけそれぞれのdelegate, datasourceを定義できる。
        switch type {
        case .Hoge:
            pickerView = HogePickerView()
        case .Huga:
            pickerView = HugaPickerView()
        }
        pickerView.dataSource = self
        pickerView.delegate = self
        pickerView.backgroundColor = UIColor.white
        return pickerView
    }
}

//導入したいpickerの数だけインスタンスを作成したいのでその分のUIPickerViewクラスを用意。
fileprivate class HogePickerView: UIPickerView {}
fileprivate class HugaPickerView: UIPickerView {}

extension SampleViewController: UIPickerViewDataSource {
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    //getPickerView(type: PickerType)関数でタイプ分けされたインスタンスを判定して、pickerに表示する個数をそれぞれreturnする
    func pickerView(_ pickerView: UIPickerView,
                    numberOfRowsInComponent component: Int) -> Int {
        switch pickerView {
        case is HogePickerView:
            return pickerForHoge.count
        case is HugaPickerView:
            return pickerForHuga.count
        default:
            return pickerForHoge.count
        }
    }
}

extension SampleViewController: UIPickerViewDelegate {
    func pickerView(_ pickerView: UIPickerView,
                    titleForRow row: Int,
                    forComponent component: Int) -> String? {
        switch pickerView {
        case is HogePickerView:
            return pickerForHoge[row]
        case is HugaPickerView:
            return pickerForHuga[row]
        default:
            return pickerForHoge[row]
        }
    }
    
    func pickerView(_ pickerView: UIPickerView,
                    didSelectRow row: Int,
                    inComponent component: Int) {
        switch pickerView {
        case is HogePickerView:
            return HogeTextField.text = pickerForHoge[row]
        case is HugaPickerView:
            return HugaTextField.text = pickerForHuga[row]
        default:
            return HogeTextField.text = pickerForHoge[row]
        }
    }
}

以上です。ちなみにswitch分の中でdefaultを定義していますが特に意味は無く、単に書かないとエラーがでるからです。なのでもっと上手な書き方などあればドシドシ意見頂きたいです。

##結論
enumは便利。今回enum以外にも工夫点はありますが、enumがなければもっと可読性が低いコードになっていたと思います。例えばtag分けしてif文で条件分岐するなど。開発を続けると今後もっとこういった場面が出てくると思うので、enumはしっかりと使いこなしたいですね。

5
6
2

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
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?