【初心者向け】UITableViewの基本的な使い方を極める【入門】
Table of Contents
初心者向けにiOSのUITableViewの基本的な使い方について説明します。 この記事を一通り読むことでほとんどの部分を極めれることを目標にしています。
Xcodeでプロジェクトを作成し、storyboardを使ってUITextFieldやUILabel, UIButtonを使って簡単な画面を作れるようになりましたか?
しかし、UITableViewを使おうと思ったら、途端に扱うクラスが増えてそれぞれの関係性がなんなのかついていけず、躓いている人が多いのではないでしょうか?
最初の躓きとなりやすいUITableViewを1つ1つ役割を理解し、それぞれの関係性を頭でイメージできるようになれば、難しいものではなくなります。
テーブルを表示してタップでアラートを出すサンプルを作成する
完成イメージは次の動画のように、テーブルが表示されタップしたらタップしたデータをアラートで表示します。
このサンプルには
- UITableView
- UITableViewDelegate
- UITableViewDataSource
- UITableViewCell
- UIAlertController
- UIAlertAction
が使われています。
全機能を使っているのではなく、部分的に使っているだけなので気構えずに1つずつ読み解いていきましょう。
UITableViewをViewControllerと紐付ける
まず次の動画のようにstoryboard上のUITableViewをコードとバインディングします。
※動画が再生出来ない場合はキャッシュ無視リロードしてみてください。chromeであればcmd+rでできます。
この時点で一度実行します。実行するとテーブルのセパレータ(区切り線)が表示されるだけですが、実行はできます。
もし実行できずエラーになる場合は、UITableViewの紐付けが次の画像のようになっているか確認してみてください。
コードは次の通りです。
ViewController.swift
import UIKit
class ViewController: UIViewController {
@IBOutlet private weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
UITableViewDataSourceとはデータをUITableViewに流し込む役割
先程の状態ではテーブルには何も表示されていません。
次の画像のようにデータを表示したいですね。
テーブルに何らかのデータを表示するには、UITableViewDataSource
を使います。
UITableViewDataSourceとは
UITableViewDataSourceとはUITableViewのデータモデルを表現するプロトコルです。 このプロトコルを使えばテーブルにデータを表示することができます。
public protocol UITableViewDataSource : NSObjectProtocol { }
データを表示するコードを書く
次のコードはテーブルにABCD1234を上から順番に表示するコードです。
class ViewController: UIViewController {
@IBOutlet private weak var tableView: UITableView!
private let items: [String] = ["A", "B", "C", "D", "1", "2", "3", "4"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ??
UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = items[indexPath.row]
return cell
}
}
順番に説明します。
表示するデータ
private let items: [String] = ["A", "B", "C", "D", "1", "2", "3", "4"]
items
がデータとなります。この並び順がテーブルの表示順となります。
UITableView(テーブル)にデータモデルを渡す
tableView.dataSource = self
tableView.dataSource
とはUITableViewが持つプロパティで型はUITableViewDataSource
です。
定義にジャンプすれば
weak open var dataSource: UITableViewDataSource?
と書いてるのを確認できます。
このUITableViewDataSource
とは前述した、
「UITableViewDataSourceとはUITableViewのデータモデルを表現するプロトコル」です。
このプロトコルに対し、self
を代入しています。 selfとはViewControllerです。
つまりViewController
がUITableViewDataSource
を採用してることになります。
ViewControllerがUITableViewDataSourceを採用する
extension ViewController: UITableViewDataSource {
// ...
}
このコード部分がそれにあたります。
セル数を返す
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
これは UITableViewDataSource
が提供する実装が必要なメソッドの1つです。
指定セクション内のロウの数を返すメソッドです。
今回はitems
の中身を表示するので、items
の要素数を返します。
section
は今回は使いませんが、詳しくはこちらの記事をどうぞ。
セクションとは?ロウとは?
次の図のような関係になってます。
- ロウ(Row)とはセルを表します。
- セクションとはロウをまとめたグループ。
セルの1つ1つを返す
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ??
UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = items[indexPath.row]
return cell
}
ここで指定された位置(indexPath: IndexPath)にどんなセルを返すのかを定義します。
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ??
UITableViewCell(style: .default, reuseIdentifier: "cell")
セルIDcell
があればそれを使いまわします。
なければUITableViewCellをスタイルdefault
でID名をcell
で用意します。
reuseIdentifier: "cell"
はセルを使い回すための識別子を名付けます。
これは複数種類のセルを扱う場合、ユニークである必要があります。
セルには予め何パターンか用意されている
style: .default
とはUITableViewCellに予め何パターンか用意されているのでそれの指定になります。
詳しくはこちらの記事で書いてあります。
セルはカスタマイズできる
既存スタイルにはなく自分でセルを自作(カスタマイズ)することもできます。
詳しくはこちらの記事をどうぞ。
セルは使い回す
UITableViewCellはIDによる種類別で用意しておき、セルを再利用することでメモリ効率を満たしています。
詳しくはこちらの記事をどうぞ。
UITableViewのUI特性はたくさんのデータを縦に積むことで、スクロールすればそれらを閲覧できます。
そのため例えばテーブルに1000個のデータを並べた場合、UITableViewCellは1000個必要になります。
それだと、メモリ効率が非常に悪いため、同じレイアウトなら使いまわそう。という考えがあります。
それがReusableCell
と名付けられた由来になります。
UITableViewDelegateとはUITableViewのイベントハンドラ
データを表示することができました。
しかしアプリとしては表示だけで済むケースはほぼなく、大抵はセルをタップしたら何かしらアクションを起きます。
次のように押されたセルに関連するアクションを出すなど。
セルのタップを検知するには、UITableViewDelegate
を使います。
UITableViewDelegateとは
UITableViewDelegate
とはUITableView
の操作イベントをハンドリングするプロトコルです。
今回はタップのみですが、それ以外にも様々なイベントを検知することができます。
詳しくはこちらにまとめてあります。
セルをタップしたらアラート出すコードを書く
次のコードはセルをタップすると選択したデータをアラートに表示するコードです。
class ViewController: UIViewController {
@IBOutlet private weak var tableView: UITableView!
private let items: [String] = ["A", "B", "C", "D", "1", "2", "3", "4"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ??
UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = items[indexPath.row]
return cell
}
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = items[indexPath.row]
let alert = UIAlertController(title: "タイトル", message: item, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}
}
前述したデータ表示のコードに加えてあります。順番に説明します。
tableView.delegate = self
tableView.dataSource = self
と同様になります。
delegate
はUITableViewDelegate
というプロトコルになります。
タップしたら呼ばれるメソッドがある
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = items[indexPath.row]
let alert = UIAlertController(title: "タイトル", message: item, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}
}
このメソッドはセルがタップされたら呼ばれるメソッドです。UITableViewDelegate
プロトコルによって定義されています。
アラートを出す
let item = items[indexPath.row]
let alert = UIAlertController(title: "タイトル", message: item, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
データアクセス方法をセル表示と同様に行うことで、タップした場所に表示されたデータ取得を実現しています。 アラートに関してはこちらにまとめてあります。
引っ張ったら更新するPull to refreshを実装する
UIRefreshControlを使って、テーブル最上部で下にスワイプしてリフレッシュを呼ぶ方法についてはこちらの記事で紹介しています。
もう少し細かい部分を良くする
基本的な部分は前述した説明とコードになります。
しかし、ついでにデザイナーから要望を受けやすい一部とその実装を簡単に説明します。
なにもないセルをなくしたい
「データが入っているセルはいいのですが、データが何も表示していないセルをなんとかできませんか?」と言われた場合、 セルに見えている原因となるセパレート(区切り線)を消すことで解決します。
override func viewDidLoad() {
super.viewDidLoad()
// ...
tableView.tableFooterView = UIView()
}
このようにフッタービューに対して空のUIViewを代入することで空セルの区切り線は表示されなくなります。
区切り線あり | 区切り線なし |
---|---|
タップした後が残るのを消して欲しい
特に何もしないとタップしてアラートが終わった後もタップした後が残ります。これを解決します。
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
// ...
}
このように deselectRow
メソッドを呼ぶことで選択解除されます。
UITableViewの使い方は奥が深い
UITableViewはiOSを習い始めでは慣れないクラス構造でつまづきやすいポイントかと思います。 しかし、アプリのレイアウトにおいてほぼ100%使われるので、極めておいて損はないです。
今回は基本的な部分の説明でしたが、実際の開発ではこの基本実装だけでは到底無理なので、 めげずにどんどんUITableViewの使い方を覚えていったほうが体系的に理解できます。