こんばんは。
今回はアプリ作成に欠かせないのに加えて、特有な設定が多くて初心者が挫折しやすいTableView
を作成する手順をまとめていきます。
画面キャプチャを多く使用して、できる限りわかりやすいように説明していきます。
この記事はこちらのswift入門のアプリ実践開発編のPart1です
Contents
今回扱うファイル
こちらの記事でプロジェクトファイルを作成しまだいじっていない状態でGitHub
のリポジトリを作成しました。
今回はこちらのプロジェクトファイルをいじっていきます。
まず前回プロジェクトファイルを作成した際に、SingleViewApplication
で作成しましたので中身のファイルは、
- AppDelegate.swift
- ViewController.swift
- Main.storyboard
- Assets.xcassets
- LaunchScreen.storyboard
- Info.plist
となっていると思いますが、今回はこれらから、
- ViewController.swift
- Main.storyboard
こちらの2つをいじってTableView
を表示できるようにしようと思います。
なお、storyboard
とviewController
は1:1で対応するように作成するのが一般的ですが、デフォルトで作成されているこの2つは既に対応するように設定されています。
Storyboardの設定
StoryBoardにTableViewを設置する
まず、Main.storyboard
を開き、右下の検索できる部分に「table」と入力したら候補に「Table View」というのがでてくるのでそれをこのようにドラッグ&ドロップします。
TableViewを画面いっぱいに設定する
次にこのままだとTableView
をただのっけただけになるので、tableView
が画面いっぱいになるように設定します。
上の画像の赤く囲ったところを押してこのように設定することができます。オートレイアウトの設定をするときに使うものです。
すると下の画像のようになります。オレンジになっているのは設定は特に問題なく設定できていますが、まだ画面上で反映されてないので赤く囲った部分を押すことで更新することができます。
下の画像のようになりました。
デフォルトでは左右にマージンが設定されているのでこれを画面横端いっぱいになるように設定します。
まずは左側の縦の青い線を一回クリックするとフォーカスを当てることができて、右側のコマみたいなボタンを押して画像のようにデフォルトでは「Relative to margin」という項目にチェックが入っているので外します。
右側も同様に行いましょう。左側のときは「Second Item」、右側のときは「First Item」のところで違う場所になっているので注意です。
設定が完了するとこのようになり、TableView
が画面いっぱいに設定することができました。
ViewControllerの設定
TableViewとViewControllerのヒモづけ
この状態では、ただStoryBoard
に貼り付けただけの状態なので、次は中身を描画するための設定をしていきます。
それにあたって、ViewController
でTableView
の中身をどうするかの設定をできるように設定する必要があります。なのでこのTableView
とViewController
のヒモ付を行います。
まずIBOutlet
を設定します。下の用に設定します。
これはTableView
の上にカーソルを合わせて、commandキーを押しながらドラッグします。
DelegateとDatasourceの設定
次にDelegate
とDatasource
を設定します。下のように設定します。Delegate
とDatasource
に関しては後ほど説明します。
設定が完了すると下の画像のように、3つのコネクションがある状態になっていれば大丈夫です。よくうまくいかなくて何回も作り直したりしていると、部品は消してもコネクションだけ残り続けて、ない部品を参照してアプリがクラッシュするっていうことに陥ることが多いのでここを確認するようにしましょう。
以上をすることで設定したViewController
でTableView
をいじれるようになりました。
TableViewCellの作成
xibファイルを作成する
つぎにセルのファイルを作成していきます。コードで書いていく方法もありますが僕らのチームではxibファイル
を作成して管理しているのでその方法を説明していきます。
コマンドNで新規ファイルを作成します。「Cocoa Touch Class」を選択します。
今回はUITableViewCell
を作成して、「Alse create XIB file」にチェックをするとXibファイル
も作成してくれてヒモ付もされた状態になっています。
StoryBoard
と同様にUITableViewCell
もレイアウトのファイルとコードを書くファイルが分かれていてヒモ付をしてセットで扱います。
Identifierの設定
ヒモ付は完了していますが、Identifier
を設定しないといけません。基本的にファイル名と同じ名前で大丈夫です。あとでTableView
にセルの登録をする際に必要になってくるものです。ここの設定が間違っていて闇に陥るケースが多いのでつづりとかはなるべくコピペしたりして間違えないようにしましょう。
Labelを設置する
とりあえずLabel
をセルの真ん中に設定しました。
縦軸横軸に対して中心に設定をこの様にすることができます。
ひとまずこんなかんじになりました。
ViewControllerの書き換え
TableViewが表示されるようにViewControllerを書き換える
それではViewController
を書き換えて実際にTableView
を表示させてみましょう。
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "TableViewCell")
}
}
extension ViewController: UITableViewDataSource {
// セクションの個数を返す
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
// セクションごとにセルの個数を返す
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
// セルの中身を返す
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: TableViewCell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
return cell
}
// セルの高さを返す
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
}
extension ViewController: UITableViewDelegate {
// セルがタップされたときの処理を書く
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
return
}
// スクロールしたときの処理を書く
func scrollViewDidScroll(_ scrollView: UIScrollView) {
return
}
}
まずこのように書きました。
そうするととりあえずこのように表示されました。
とりあえず動いてそうなので一安心です。
delegateとは
コードを1つ1つ説明していこうと思いますがその前にdelegate
の説明をしていきます。
delegate
を和訳すると「移譲」という意味で、swift的には実装を託すという意味合いになります。
TableView
でいうと、今回の実装方法ではTableView
の移譲先をViewController
に設定しました。
TableView
では、あとは表示させるセルの個数とか中身とかタップしたらどうするかだけ教えてくれれば、その通りに実現するように実装したからあとはその実装だけよろしく、というようなかんじでViewController
に託しています。
すき家でサイズとトッピングだけ指定してくれれば牛丼がつくられるみたいなかんじですね。
先程Delegate
とDataSource
の2つを設定しました。それぞれで使い道が異なっています。
DataSource
ではセルの個数や中身、Delegate
ではイベント発生時の処理を移譲するようになっています。
それぞれ例えばDataSource
では表示させるセルの個数を設定するメソッドが用意されていたり、Delegate
ではタップされたときの処理を書くメソッドが用意されています。
先程の例ではよく使うメソッドをそれが何をするものなのかを書いていきました。この他にも細かいものはたくさんあるので興味があったら調べてみてほしいです。
他の言語をやってる人で、メソッド名が同じものが多かったり、これらのメソッドの呼び出し元が一切かかれてないじゃないことに違和感がある人もあるとは思いますが、引数の中身がそれぞれ違っていて、引数や返り値で何をやっているのかおおよそ検討がつくのではないのかなと思います。
あと先程移譲の話をしたと思いますが、ViewController
で定義したメソッドたちはTableView
に呼ぶ処理が書かれていて、それぞれ適切なタイミングでViewController
に設定されたメソッドを呼ぶ実装がTableView
になされています。
IndexPathとは
あとIndexPath
について説明します。
cellForRowAtメソッド
やdidSelectRowAtメソッド
ではindexPath
がメソッド内で使えます。
indexPath
というのは対象のセルのセクションとインデックスを持っています。
didSelectRowAtメソッド
ではセルがタップされたときに呼ばれるメソッドで、タップされたセルのセクションとインデックスがindexPath
に入っています。
cellForRowAt
は1つ1つのセルを描画する際に呼ばれて、表示しようとしてるセルの個数分だけ個別に呼ばれます。作られようとしてるセルに対してそのセルのセクションとインデックスがindexPath
に入っています。
少し書き換えてみる
先程の例では表示はできましたが、すごくしょぼい結果なのでindexPath
を踏まえて少し書き換えてみます。
まず、Cell
をこのように書き換えました。
import UIKit
class TableViewCell: UITableViewCell {
@IBOutlet weak var label: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func bindData(text: String) {
label.text = text
}
}
これをすることで、Label
の文字をbindData
に渡した文字に書き換えることができます。
ViewController
はこのように書き換えました。
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "TableViewCell")
}
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: TableViewCell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
cell.bindData(text: "section: \(indexPath.section) index: \(indexPath.row)")
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("section: \(indexPath.section) index: \(indexPath.row)")
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
return
}
}
このように書き換えるとどうなるかをイメージしてみてほしいです。
下のようになりました。
一番上のセルをタップしたらちゃんとプリントされています。
今回はTableView
が簡単に扱えるようになりました。
次回はapi
から表示するためのデータを取得しそれをTableView
に表示させるのをやりたいと思います。
今回のコードはこちらでも確認できるのでよかったらご確認ください。