XibファイルでTableViewを実装する

TableViewを実装する際に画一的な見た目のセルではなく、一つ一つのセルをカスタマイズしたい時があるかと思います。

僕の場合は、個人アプリのホーム画面にXibファイルで作成したTableViewを採用していますが、結構色んなことができて非常に便利でした。

今後、他のアプリでも活用する機会が増えると思ったので、今回はこれについて書いていきたいと思います。

# できたもの
カスタムセルを使い、異なるデザイン(とは言えLabelとHeightが異なるだけ)のセルをtableView上に描画しています。

![CustomTableView.gif](https://qiita-image-store.s3.amazonaws.com/0/143619/795eacb9-e189-65fd-cb8d-548ae6cfe81e.gif)

# 実装
このソースは[Github](https://github.com/takuyama29/CustomTableView)でも公開しているので、ぜひこちらも見てみて下さい。

### Viewを追加
HeaderViewとTableViewをそれぞれStoryBoardに追加します。

<img width="1279" alt="Screen Shot 2017-09-08 at 9.34.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/143619/7038a9f1-b2d4-b98c-ec7e-15cb5bb86ba6.png">

### Xibファイル追加
次にTableViewに表示したいCellの数だけXibファイルを追加していきます。
僕の場合は`Xib`フォルダーを追加してその配下にXibファイルとswiftファイルをまとめて入れています。

またXibファイルの作成は、通常の新規ファイル追加をする時に表示されるダイアログの`Also create XIB file`にチェックをいれるだけでXibファイルが生成されます。

<img width="1279" alt="Screen_Shot_2017-09-07_at_11_10_22.png" src="https://qiita-image-store.s3.amazonaws.com/0/143619/b802f959-8f36-c8cc-50a8-26f01beac49b.png">

これで作成したXibファイルでセルを自由にカスタマイズできるようになりました。

<img width="1280" alt="Screen Shot 2017-09-08 at 9.49.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/143619/99280c7d-6d6c-96fd-93b4-ddb105754d3d.png">


### CellのHeightを可変にする
次にカスタマイズしたCellのHeightを可変にする処理を行います。

TableViewを配置しているViewControllerのviewDidLoad()内に下記のメソッドを追記します。

```ViewController.swift
override func viewDidLoad() {
    super.viewDidLoad()
    
    self.tableView.estimatedRowHeight = 100
}

```

`estimatedRowHeight`はTableViewCellの推定高さを指定し、更に高さが変わるCellの場合には自動的に高さを調整する役割を持ちます。

この時に指定するデフォルトの高さは任意で問題ないので今回は100としています。

### カスタムセルを描画する
ここまででカスタムセルの作成とTableViewへ描画する際の準備が整いました。
次に行うのは、カスタムセルをTableViewへ描画する処理です。

TableViewを設置しているViewControllerのviewDidLoad()内に下記を追加します。

```ViewController.swift
var tableViewArray = [UITableViewCell]()

...

override func viewDidLoad() {

    self.tableView.register(UINib(nibName: "FirstTableViewCell", bundle: nil), forCellReuseIdentifier: "FirstTableViewCell")

    guard let firstTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "FirstTableViewCell") as? FirstTableViewCell else {
        return
    }
    self.tableViewArray.append(firstTableViewCell)

} 
```

`tableViewArray`は後からセルを描画する時の処理に活用するので、ここで追加処置を入れておきます。

また、上記は`FirstTableViewCell`のみ追加していますが、作成したカスタムセルの分だけこの処理を追加してください。

### TableViewのメソッドを追加
TableViewは下記の2つのメソッドの実装が必須となっています。
1. tableView(_ tableView: UITableView, numberOfRowsInSection)
2. tableView(_ tableView: UITableView, cellForRowAt)

今回カスタムセルを描画するTableViewでは、`FirstTableViewCell`、`SecondTableViewCell`、`ThirdTableViewCell`の3つのセルを描画するため、下記の様な実装となります。

```ViewController.swift
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    
    return tableViewArray.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let tableViewCell = self.tableViewArray[indexPath.row]
    
    if tableViewCell is FirstTableViewCell {
        return tableViewCell
        
    } else if tableViewCell is SecondTableViewCell {

        return tableViewCell
        
    } else if tableViewCell is ThirdTableViewCell {
        
        // no selectable
        tableViewCell.selectionStyle = UITableViewCellSelectionStyle.none
        return tableViewCell
        
    }
    
    return UITableViewCell()
}
```
※`.selectionStyle`に`UITableViewCellSelectionStyle.none`を指定すると、セルタップ時に選択できなくすることができるので例として書いています。この処理を入れているので、先に紹介した`できたもの`ではThirdTableViewCellが選択状態になっておりません。


### ヘッダービューに選択されたセルの名前を出力
TableViewでは、セルが選択された時に呼び出されるメソッドがあるので、そこで選択されたセルの名前を描画する様にします。

```ViewController.swift
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    headerView.text = tableView.cellForRow(at: indexPath as IndexPath)?.className
    
}
```
※`.className`は独自に追加したextensionを活用しています(以下参照)。

```
import Foundation

extension NSObject {
    class var className: String {
        return String(describing: self)
    }
    
    var className: String {
        return type(of: self).className
    }
}
```

ここまでで最初に`できるもの`で提示したTableViewの描画が完成です。

# おまけ
先ほどまでの処理で作成したTableViewのままでも充分活用できるのですが、もう少しカスタマイズを加えたいと思います。

例えばセルの線を消したり、セルをグループ化したりといったことができます。

### セルの線削除
作成したカスタセルのawakeFromNib()内に下記の処理を追加します。

```FirstTableViewCell.swift
separatorInset = UIEdgeInsets(top: 0, left: bounds.width, bottom: 0, right: 0)
```

カスタムセルの線を全て削除すると下記の様になります。

<img width="375" alt="IMG_3534.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/143619/bbbb6860-7f76-21e6-395b-3f8b128c9df3.png">

### セルのグループ化
セルのグループ化を行うと、下記の不要なセルを非表示にして表示されているセルを１つのパーツとしてグループ化できます。

Main.storyboard > TableView > Attributes inspector > Style > Grouped

<img width="1280" alt="Screen_Shot_2017-09-08_at_10_51_30.png" src="https://qiita-image-store.s3.amazonaws.com/0/143619/e992c4a5-0332-8c3c-dc2a-016dab65c6d3.png">


<img width="375" alt="IMG_3532.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/143619/c52cca56-a8a0-e698-37c6-ce71c45f7ff6.png">

この状態だとTableViewの上部に不要なセル(ヘッダービュー下の灰色のセル)が表示されてしまいます。
これを削除したい場合は、TableViewを設置しているViewController内で下記を追記してください。

```ViewController.swift
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    // Erase header cells
    return .leastNormalMagnitude
}
```

これで見た目も大分すっきりしました。

この他にもTableViewで表現できることはたくさんありますので、興味ある方はぜひ調べてみて下さい。