##はじめに
ジーズアカデミー卒業生のyukingと申します。今回SwiftにおけるPickerViewの実装方法を前回投稿したものの改善版として投稿させていただきます。
##躓いた点
1つのViewControllerに2つ以上のUIPickerViewを導入しようとした時に、ふと「あれ?複数のUIPIckerViewを表示って難しくね?考えたらdelegateで複数個なんて返せない.....」と思ったことがきっかけです。
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こちらの記事を参考にしてみてください。
それでは具体的にコードを記していきます。
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はしっかりと使いこなしたいですね。