【iOS】UITableViewのSectionHeaderとSectionFooterをdequeuで使いまわしできるようにする


## iOS その３ Advent Calendar

3日目も書かせていただきます！

予約投稿最高！

ちなみに前日も書かせていただきました！

では本題です。

## UITableViewのSectionHeaderとSectionFooterをdequeuで使いまわしできるようにする

多分難しいことを考えなければ以下の`UITableViewDelegate`を使って表示していると思います。

僕もそうでした。

```swift:ViewController.swift
class ViewController {
    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.tableView.delegate = self
    }
}

extension ViewController: UITableViewDelegate {

    func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        // 適当な高さ
        return 80.0
    }

    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return CustomView()
    }

}
```

という感じかと思います。

ただそうすると以下の問題があります。

- 他のイベント時に*Section Header*, *Section Footer*にアクセスすることができません。
- なんか毎回生成処理するのめんどくさい

### ちなみにですが...

`UITableView`には以下のメソッドがあります。

- `func headerView(forSection section: Int) -> UITableViewHeaderFooterView?`
- `func footerView(forSection section: Int) -> UITableViewHeaderFooterView?`

というメソッドがありますが、上記の作り方だとアクセスしても`nil`が返ってくるだけです。

このメソッドからアクセスできるようにしたいと思います。

## UITableViewHeaderFooterView

### 最初はクラスで

`UITableViewHeaderFooterView`を継承したクラスを作成して`register`処理をします。

以下のようなクラスを作成したとします。

```swift:CustomTableViewHeaderFooterView.swift
class CustomTableViewHeaderFooterView: UITableViewHeaderFooterView {
    // 適当にレイアウト処理を施しているであろうLabel
    private let label: UILabel

    func setText(_ text: String?) {
        self.label.text = text
    }

}
```

これを大好きregister処理をします。

```swift
class ViewController {

    @IBOutlet weak var tableView: UITableView!

    // identifier
    private let headerfooterViewIdentifier = "CustomTableViewHeaderFooterView"

    override func viewDidLoad() {
        super.viewDidLoad()
        self.tableView.delegate = self

        self.tableView.register(CustomTableViewHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: headerfooterViewIdentifier)
    }

}
```

で使うときは

```swift
extension ViewController: UITableViewDelegate {

    func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        // 適当な高さ
        return 80.0
    }

    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let view = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerfooterViewIdentifier)
        if let v = view as? CustomTableViewHeaderFooterView {
            v.setText("header!!!")
        }
        return view
    }

}
```

となります。

### それではNibファイルを使って

やってみます。

以下のような`CustomTableViewHeaderFooterView.xib`を作成してください。

![スクリーンショット 2016-11-28 0.25.22.png](https://qiita-image-store.s3.amazonaws.com/0/47302/37ac5d1b-733e-efc6-c9e6-f39104c3322f.png)

接続していきます。

<img width="833" alt="スクリーンショット 2016-11-28 0.30.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/47302/501d0945-7c86-3760-5401-dd5c939ec4c2.png">

これを使用していきます。

```swift
class ViewController {

    @IBOutlet weak var tableView: UITableView!

    // identifier
    private let nibName = "CustomTableViewHeaderFooterView"

    override func viewDidLoad() {
        super.viewDidLoad()
        self.tableView.delegate = self
        
        let nib = UINib(nibName: nibName, bundle: nil)
        self.tableView.register(nib, forHeaderFooterViewReuseIdentifier: nibName)
    }

}
```

で使うときは

```swift
extension ViewController: UITableViewDelegate {

    func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        // 適当な高さ
        return 80.0
    }

    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let view = tableView.dequeueReusableHeaderFooterView(withIdentifier: nibName)
        if let v = view as? CustomTableViewHeaderFooterView {
            v.setText("header!!!")
        }
        return view
    }

}
```

となります。

ちなみに他のイベントなどでsectionの色を変えたいなどの場合は

```swift
// 取得したいseciton
let section = 0
let view = self.tableView.headerView(forSection: section)
view.backgroundColor = UIColor.blue
```

というように扱うことができます。

## 終わりに

僕はこれでUITableViewのスクロールに合わせてSectionに下線を表示する、しないを実装しました。

## 参考

- [UITableView - UIKit | Apple Developer Documentation](https://developer.apple.com/reference/uikit/uitableview)
- [UITableViewHeaderFooterView - UIKit | Apple Developer Documentation](https://developer.apple.com/reference/uikit/UITableViewHeaderFooterView)