Swift - Xibを利用してTableViewとカスタマイズcellを作成する

#はじめに
恐らくtableviewなら、普通のやり方だけでいけるのに、
どうしてわざわざ手間をかけてxibを使うの？
どんなタイミングで使うの？と思う方がいると思います。

シンプルな画面なら普通のやり方で大丈夫だと思いますが、
同じController画面に複数の複雑なメイン画面がある時、このように各画面をパーツ化し、それぞれ組み立てた方がわかりやすくて今後の保守性、拡張性に優しいかもしれませんので、ご参考を！
#手順
- Xibファイルを作成する
- Xib画面にUITableViewを追加する
- カスタマイズclassのファイルを作成する
- cell用のxibファイル、swiftファイルを作成する
- controller画面のUIViewのclassを定義したclassと設定する
- viewDidLoadでcellをtableviewに登録する処理を入れる

各ステップに細かい手間があって抜けてはいけないので、以下説明いたします。

#Xibファイルを作成する
File -> New -> File...でXibファイルを作成する
![截圖 2021-09-23 19.08.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1326861/f6399f71-70e9-8f63-fd9b-a8cccc2ccbb5.png)
#Xib画面にUITableViewを追加する
XibのviewにUITableViewを入れる
![截圖 2021-09-23 19.14.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1326861/975b98a8-7448-a19a-efe2-c409459a3343.png)
#カスタマイズclassのファイルを作成する
File -> New -> File... -> Swift Fileを選択する
ファイルを作成し、カスタマイズclassを定義してから、xibファイルの**File's Owner**のclassを定義したclassと設定して、以下のように処理を入れる

```test.swift
import Foundation
import UIKit

class Test: UIView{
    //xibファイルに追加したUItableViewをswiftファイルと連結するようにする
    @IBOutlet weak var testTableView: UITableView!

    // コードから呼び出す際に使用される
    override init(frame: CGRect) {
        super.init(frame: frame)
        loadNib()
    }
    
    // Storyboardから利用する際に使用される
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        loadNib()
    }
    
    // 上記どちらのinitからも使用される共通関数で、xibファイルを呼び出す。
    func loadNib() {
        // 第1引数のnameには、xibファイル名
        // ownerはself固定
        // optionsはここではnil
        let view = Bundle.main.loadNibNamed("test", owner: self, options: nil)?.first as! UIView
        view.frame = self.bounds
        self.addSubview(view)
    }
}
```
#cell用のxibファイル、swiftファイルを作成する
File -> New -> Cocoa Touch Classを選択し、「Also create XIB file」にチェックを入れて、xibファイル、swiftファイル両方作れる。

作った後に、色々UIコンポネートを入れて、Identifierをネーミングする
![截圖 2021-09-23 21.20.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1326861/0753443f-d4cc-ef28-a3a6-642650d4340a.png)
こちらの場合は二つのラベルを入れたので、swiftファイルと連結するようにする

```testTableViewCell.swift
import UIKit

class testTableViewCell: UITableViewCell {

    //swiftファイルと連結するようにする
    @IBOutlet weak var testLb1: UILabel!
    @IBOutlet weak var testLb2: 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
    }
    
}
```

#定義したclassを持つUIViewをcontroller画面に入れる
UIViewを入れて、Custom ClassをTestにする。
![截圖 2021-09-23 20.59.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1326861/455c1aa8-684d-fdbe-8567-ac550ab82c56.png)
#viewDidLoadにcellをtableviewに登録する処理を入れる
登録する前に、追加したUIViewをswiftファイルと連結するようにする

```ViewController.swift
    @IBOutlet weak var testView: Test!
```
連結した後に、viewDidLoadでcellをtableviewに登録する

```ViewController.swift
import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var testView: Test!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        testView.testTableView.register(UINib(nibName: "testTableViewCell", bundle: nil), forCellReuseIdentifier: "testCell")
    }

}
```
登録した後に、あとは従来のTableView追加のやり方とほぼ同じ。
①`UITableViewDelegate`、`UITableViewDataSource`をViewControllerに実装して、`dataSource `、`delegate`をViewControllerに代理させる

```ViewController.swift
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
    override func viewDidLoad() {
        super.viewDidLoad()
        testView.testTableView.dataSource = self
        testView.testTableView.delegate = self
        testView.testTableView.register(UINib(nibName: "testTableViewCell", bundle: nil), forCellReuseIdentifier: "testCell")
    }
...
```

②`func tableView(_ tableView:numberOfRowsInSection section:) -> Int`、`func tableView(_ tableView:cellForRowAt indexPath:) -> UITableViewCell`を実装する

```ViewController.swift
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    //適当に戻す
    return 10
    }
    
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "testCell", for: indexPath) as? testTableViewCell
    else { return UITableViewCell() }
    cell.testLb1.text = "test1"
    cell.testLb2.text = "test2"
    return cell
    }
```
これで完成！
![截圖 2021-09-23 21.56.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1326861/aa3def5a-40f4-3f1d-54a4-8860497c9b2e.png)
#終わりに
メイン機能だけ説明しました、細かいレーアウトの処理はここで割愛いたします。
tableviewだけではなく、他にさまざまなUIViewでも、このやり方で実装できると思います。
これで説明が終えたいと思います、何か抜けた部分があればコメントいただければと思います。