SwiftでイージーメンテナンスなUITableViewを実装する
この記事は アイソルート Advent Calendar 2023 19日目の記事です。
こんにちは。モバイルソリューショングループのonodera.tです。
今回はSwift UITableViewのメンテナンス性、可読性が高い実装方法を紹介します。
個人的に、一昔前までUITableViewのdelegateメソッド内部で毎回switch文を入れており、メンテナンス性も悪くコードも見にくい実装になっていました。
今回は二重配列と構造体を利用して簡潔かつメンテナンス性の高い実装方法を考えました。
目次
switch文を使用した実装例
delegateメソッドにswitch文を使用する例を紹介します。
セル生成のtableView cellForRowAtメソッドを例として、タイトルに文字をセットしてセルをreturnする実装してみます。
// セル生成処理
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MenuCell", for: indexPath) as! MenuCell
switch indexPath.section {
case 0:
switch indexPath.row {
case 0:
cell.titleLabel.text = "aaa"
return cell
case 1:
cell.titleLabel.text = "bbb"
return cell
default:
break
}
case 1:
switch indexPath.row {
case 0:
cell.titleLabel.text = "AAA"
return cell
case 1:
cell.titleLabel.text = "BBB"
return cell
default:
break
}
default:
break
}
return cell
}
例ではセクション2つ、セル2つで書いていますが既にだいぶ長いですね。。
他のdelegateメソッドでも同じような実装になると更に長くなり読みづらくなってしまいます。
二重配列と構造体の使い方
次は二重配列と構造体を使用した実装になります。
構造体にはセル1つ分に持たせたい情報を定義します。
今回はタイトル文字と、タップした時にブラウザ遷移する想定でURLの文字を持たせています。
// 1つ分のセル情報を持つ構造体
struct CellData {
let text:String
let url:String
}
次にこの構造体を元にセルに持たせるデータを整形してみます。
二重配列の子配列では1セクション内のセル情報を格納、親配列から見るとセクションごとの子配列の集合が格納されている形です。
let cellData = [
[
CellData(text: "aaa", url: "aaa"),
CellData(text: "bbb", url: "bbb"),
],[
CellData(text: "AAA", url: "AAA"),
CellData(text: "BBB", url: "BBB"),
],
]
ここで作ったデータを元に、先ほどのセル生成処理を実装してみます。
// セル生成処理
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MenuCell", for: indexPath) as! MenuCell
cell.titleLabel.text = cellData[indexPath.section][indexPath.row].text
return cell
}
二重配列にセクション、列が何番目か渡すことによって生成処理が簡単に実装できるようになりました。
テキスト以外にも情報をセットしたい場合は構造体に要素を増やせばokです。
2つ以上の情報をセルにセットする場合、セル側で以下のようにCellDataクラスを引数とする処理を持たせておくと便利でしょう。
func setupCell(cellData:CellData) {
// セルに文字や画像などセットする
}
実装意図と注意点
ここまで読んでいただいて「単純にIndexPathを渡せばデータが返ってくるようなメソッドを用意すればいいのでは?」と思った方がいるかと思います。
二重配列を採用した実装意図として「セクション数」と「セクションごとの列数」がわかるというメリットがあります。
具体例として、セクション数と列数を指定するdelegateメソッドの実装例を記載します。
// セクションごとの列数を返却するdelegateメソッド
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellData[section].count
}
// セクション数を返却するdelegateメソッド
func numberOfSections(in tableView: UITableView) -> Int {
return cellData.count
}
二重配列使うことでセクション数と列数のどちらも管理できるためこのように実装できます。
もしセルを増やしたい!となった場合でも、cellDataを変更するだけでdelegateメソッドの実装側は一切変更なくメンテナンス性の高さを担保できます。
CellDataに遷移先としてurlも定義していたので、セルをタップした際の遷移処理も参考として載せておきます。
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
guard let url = URL(string: cellData[indexPath.section][indexPath.row].imageName!) else {
return
}
UIApplication.shared.open(url)
}
注意点ですが、本記事ではセクション数が2つ以上を想定して作っています。
セクション数が1つで確定している場合は二重配列ではなくただの配列としてセルデータを用意する方がよりスマートに実装できるでしょう。
まとめ
ここまで、二重配列と構造体を利用したUITableViewの実装例でした。
実装例ではタップ処理がurl遷移一種類と想定していましたが、実際はセルごとに遷移処理が異なるパターンがほとんどだと思います。
その場合は構造体に遷移処理を直接持たせると逆に分かりづらくなってしまうので、処理パターンがわかるkeyなどを持たせておくといいかもしれないですね。
詳しい実装方法はまた別の記事でご紹介できればと思います。
ここまで読んでいただきありがとうございました。
UITableView実装の際、ご参考になれば幸いです。