[iOS UIKit] UIPickerView×CustomViewを入門する
はじめに
この記事ではUIPickerViewに表示する内容をUINibを用いて作成します。また、UINibを用いて独自にデザインしたビューをこの記事ではCustomViewを呼ぶことにします。
また、前の記事の続きになっているので良ければそちらもご確認ください。→https://zenn.dev/fummicc1/articles/d14dfba7e228e3
完成
下の画像のように枠線と影をつけることで要素ごとの区切りが明確なPickerViewを作成していきます。

全体の流れ
手順
CustomViewの作成(Xibファイル)
最初にUIPickerViewに表示するビューのファイルを作成します。
MacのメニューバーからFile→New→Fileと選択をし、以下の画面からViewを選びます。

画面右のNextを選択すると、ファイルの名前を入力する画面に移動します。実際は好きな名前で構いませんが、説明上CustomPickerContentという名前で作成した程で話を進めます。
CustomViewの作成(Swiftファイル)
次に作成したXibファイルに対してSwiftファイルも作成します。
Xibファイルの作成と同様にMacのメニューバーからFile→New→Fileと順に選択肢、以下の画像にあるようにCocoa Touch Classを選択します。

Xibファイルで行ったように画面右下のNextを選択すると、ファイルの名前を入力する欄が出てきますが、Xibファイルの名前を揃えて、CustomPickerContentという名前で作成しておきます。また、Subclass ofがUIViewに設定されていること、LanguageがSwiftに設定されていることを確認してください。

CustomViewのレイアウト作成
次に、作成したXibファイル(CustomPickerContent.xib)を編集していきます。
今回は枠線と影をつけたいのですが、それらの実装はSwiftファイルで行うのでXibファイルにはUILabelを一つだけ配置しておきます。

CustomViewの実装
次にCustomPickerContent.swiftを変更して、ラベルの関連付けや、影・枠線の実装を行います。
コメントを振ってありますが、具体的なコードの説明に関しては本題からずれてしまうので割愛します。
import UIKit
class CustomPickerContent: UIView {
// Xib上に配置したUILabel
@IBOutlet var label: UILabel!
// Xibが読み込まれると実行される
override func awakeFromNib() {
super.awakeFromNib()
// 影をつけている
// 影のずれを設定
layer.shadowOffset = CGSize(width: 0, height: 2)
// 影の不透明度を設定(0~1の範囲で大きいほど影が濃い)
layer.shadowOpacity = 0.4
// 影の色を設定
layer.shadowColor = UIColor.gray.cgColor
// 枠線をつけている
layer.borderWidth = 2
}
}
CustomViewのSwiftファイルとXibファイルを紐付ける
次に、CustomPickerContent.swiftとCustomPickerContent.xibを紐づけていきます。
最初にXibファイルを開いて、以下のようにCustomViewのCustom Classという項目にCustomPickerContentと入力します。

Custom Classが設定されるとそのSwiftファイルにある@IBOutletや@IBActionと関連付けができるようになるので、以下のように関連付けを設定します。

UIPickerViewDelegateでCustomViewを読み込む
最後にViewController側からCustomViewを読み込みます。UIPickerViewDelegateにある下記のメソッドを用いることでCustomViewを設定できます。
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView
記述する処理は以下のようになります。
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
// Xibファイルを読み込む
let customView = UINib(nibName: "CustomPickerContent", bundle: nil).instantiate(withOwner: nil, options: nil).first as! CustomPickerContent
// 高さを40に固定
customView.frame.size.height = 40
// PickerViewに応じて表示するデータを設定する
if pickerView == foodPickerView {
customView.label.text = foodArray[row]
} else if pickerView == drinkPickerView {
customView.label.text = drinkArray[row]
}
return customView
}
UINibというクラスより、xibファイルに作成したビューを読み込むことができます。
nibNameという引数には読み込みたいXibのファイル名を指定しています。
(また、今回の記事では触れませんが、reusing view: UIView?という引数には以前にUINibより作成したビューが格納されているので、そちらを再利用することができます。)
row引数には対象の要素の行番号は格納されているので、前記事で扱ったようにdrinkArray[row]やfoodArray[row]で表示したい文字列を取得できます。
前記事でも説明しましたが、今回はUIPickerViewが一つのViewControllerに二つ存在しているため、if文を用いてどちらのPickerViewに対して操作をしているかを分岐する必要があります。
完成(再掲)

コード全貌
GitHubにアップロードしました
Discussion