【Swift】複数のUIPickerViewを実装する

## はじめに

どうも、プログラミング未経験で学習中の**はるさん**です。
今回はStoryboardで**複数のUIPickerViewを実装する**について説明していきたいと思います。

一つの画面に複数のUIPickerViewを表示する際になかなか手こずってしまいました。
 理解すれば簡単に実装できます。

### 記事の対象者

- Swift学習を初めたばかりの方
- UIPickerViewを複数設置でお困りの方

### 記事を執筆時点での筆者の環境
 - Xcode 14.2
- Swift 5.5.2
- iOS 16.2

## 1.例題内容

### 1-1. 条件
1. 画面に二つのUIPickerViewと二つのUILabelを配置
2. UIPickerViewで選択した値をUILabelを表示させる
3. 値は「動物」と「果物」とする

### 1-2. 全体のコード
この段階では「動物」の値しか表示させることができません。
animalPickerViewとfruitUIPickerViewの両方に同じ値が出力されます。
```swift
import UIKit

class ViewController: UIViewController {

    //1個目、動物のUIPickerViewの選択内容を表示するUILabel
    @IBOutlet weak var animalLabel: UILabel!

    // 1個目、動物のUIPickerView
    @IBOutlet weak var animalPickerView: UIPickerView!

    // 2個目、フルーツのUILabel
    @IBOutlet weak var fruitLabel: UILabel!

    // 2個目、フルーツのUIPickerView
    @IBOutlet weak var fruitUIPickerView: UIPickerView!

    // 1個目、動物のUIPickerViewに表示する値の配列
    var animalValue = ["犬", "猫", "兎", "狐"]

    // 2個目、フルーツのUIPickerViewに表示する値の配列
    var fruitValue = ["リンゴ", "オレンジ", "ブドウ", "バナナ"]

    override func viewDidLoad() {
        super.viewDidLoad()
        // animalPickerViewとfruitUIPickerViewのdataSourceとdelegateをセット
        animalPickerView.dataSource = self
        animalPickerView.delegate = self
        fruitUIPickerView.dataSource = self
        fruitUIPickerView.delegate = self
    }
}

// UIPickerViewに表示設定
extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource {

    // UIPickerViewの列の数
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }

    // UIPickerViewの行数（要素の数）
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return animalValue.count
    }

    // UIPickerViewに表示する配列（内容）
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return animalValue[row]
    }

    // UIPickerViewが選択された時の挙動
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        animalLabel.text = animalValue[row]
    }
}
```
### 1-3. シュミレーターで確認
![前提のシュミレーター.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058346/afc8a0db-79e9-fc32-779f-df45e8f923bb.gif)

## 2. if文で分岐処理する

### 2-1. 変更したコード
```swift
// UIPickerViewに表示設定
extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource {

    // UIPickerViewの列の数
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }

    // UIPickerViewの行数（要素の数）
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if pickerView === animalPickerView {
            return animalValue.count
        } else if pickerView === fruitPickerView {
            return fruitValue.count
        } else {
            return animalValue.count
        }
    }

    // UIPickerViewに表示する配列（内容）
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        if pickerView === animalPickerView {
            return animalValue[row]
        } else if pickerView === fruitPickerView {
            return fruitValue[row]
        } else {
            return animalValue[row]
        }
    }

    // UIPickerViewが選択された時の挙動
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        if pickerView === animalPickerView {
            animalLabel.text = animalValue[row]
        } else if pickerView === fruitPickerView {
            fruitLabel.text = fruitValue[row]
        } else {
            animalLabel.text = animalValue[row]
        }
    }
}
```

### 2-2. 解説
手法としてはデリゲートメソッド内にif文で処理を分岐させます。この時の対象を
オブジェクトである`animalPickerView`と`fruitUIPickerView`にします。
演算子を`==`でも動きますが、オブジェクトを対象とする場合は参照比較演算子で
ある`===`とすると安全です。

### 2-3. シュミレータで確認
![if文で分岐シュミレーター.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058346/6c53b163-30f7-9117-a874-3dbe9bf6d9bc.gif)

### 2-4. 全体のコード
```swift
import UIKit

class ViewController: UIViewController {

    //1個目、動物のUIPickerViewの選択内容を表示するUILabel
    @IBOutlet weak var animalLabel: UILabel!

    // 1個目、動物のUIPickerView
    @IBOutlet weak var animalPickerView: UIPickerView!

    // 2個目、フルーツのUILabel
    @IBOutlet weak var fruitLabel: UILabel!

    // 2個目、フルーツのUIPickerView
    @IBOutlet weak var fruitPickerView: UIPickerView!

    // 1個目、動物のUIPickerViewに表示する値の配列
    var animalValue = ["犬", "猫", "兎", "狐"]

    // 2個目、フルーツのUIPickerViewに表示する値の配列
    var fruitValue = ["リンゴ", "オレンジ", "ブドウ", "バナナ"]

    override func viewDidLoad() {
        super.viewDidLoad()
        // animalPickerViewとfruitUIPickerViewのdataSourceとdelegateをセット
        animalPickerView.dataSource = self
        animalPickerView.delegate = self
        fruitPickerView.dataSource = self
        fruitPickerView.delegate = self

    }
}

// UIPickerViewに表示設定
extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource {

    // UIPickerViewの列の数
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }

    // UIPickerViewの行数（要素の数）
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if pickerView === animalPickerView {
            return animalValue.count
        } else if pickerView === fruitPickerView {
            return fruitValue.count
        } else {
            return animalValue.count
        }
    }

    // UIPickerViewに表示する配列（内容）
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        if pickerView === animalPickerView {
            return animalValue[row]
        } else if pickerView === fruitPickerView {
            return fruitValue[row]
        } else {
            return animalValue[row]
        }
    }

    // UIPickerViewが選択された時の挙動
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        if pickerView === animalPickerView {
            animalLabel.text = animalValue[row]
        } else if pickerView === fruitPickerView {
            fruitLabel.text = fruitValue[row]
        } else {
            animalLabel.text = animalValue[row]
        }
    }
}
```

## 3. tagでも判別できる
Storyboardの設定でtagという機能があります。 これを使って判別する方法もあります。
当初はこちらを使っていましたが、
Twitterで`UIPickerView`のインスタンスで判別できることを教えていただきました。
どちらでも望んだ挙動は実現できますが、個人的にtagはおすすめしません。
デメリットとして実装までの行程が増える、コードとしての可読性が下がる点が挙げられます。
一応簡単にではありますが、tagを使った実装も載せておきます。

### 3-1. StoryboardでUIPickerViewにtagをつける
ここを設定しないとtagが反映されません。

![タグづけ１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058346/23515f04-eb23-4f58-9b68-7815274887b3.png)



![タグづけ２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058346/ba718d79-65d6-3a8a-88ba-5caa23ae6473.png)



### 3-2. `viewDidLoad`内でtagを登録
```swift
    override func viewDidLoad() {
        super.viewDidLoad()
        // animalPickerViewとfruitUIPickerViewのdataSourceとdelegateをセット
        animalPickerView.dataSource = self
        animalPickerView.delegate = self
        fruitPickerView.dataSource = self
        fruitPickerView.delegate = self
        animalPickerView.tag = 1
        fruitPickerView.tag = 1
    }
}
```
### 3-2. デリゲートメソッドで処理を書く
```swift
extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource {

    // UIPickerViewの列の数
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }

    // UIPickerViewの行数（要素の数）
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if pickerView.tag == 1 {
            return animalValue.count
        } else if pickerView.tag == 2 {
            return fruitValue.count
        } else {
            return animalValue.count
        }
    }

// 以下のメソッド内も同様に書き換える。省略
```

## 終わりに
いかがだったでしょうか？ 複数のUIPickerViewが実装できれば、アプリ開発において
レイアウトの幅も広がります。
この記事が初学者の方々の学習において、少しでもお役にたてれば幸いです。

## 参考記事
[[iPhone] UIPickerView の基本的な設定 | iOS アプリ開発](https://i-app-tec.com/ios/uipickerview.html)
[[Swift5]同じビュー内に違う値を持つPickerViewを2つ表示する方法 | えとログ](https://etolog.jp/archives/pickerview_memo.html)