UITableView セルのサイズを自動的に変更する

## スタータープロジェクト

スタータープロジェクトでは、テーブルビューのセルに `UITextView` が一つだけあり、それには次のようなビューの制約（レイアウト）が含まれています：

<img width="294" alt="Screen Shot 2020-05-31 at 2.42.41 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/c788d097-8f47-3e00-4924-747274a0993e.png">

このプログラムビュー `Storyboard` では、テキストが表示されるはずです。

<img width="408" alt="Screen Shot 2020-05-31 at 2.56.21 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/01bf8610-08fa-42d2-b5da-ed044ba826cf.png">

しかしプログラムを実行しても画面には何も表示されません。テーブルビューのセルの高さが０になっているようです。

<img width="360" alt="Simulator Screen Shot - iPhone 11 - 2020-05-31 at 15.00.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/f3595424-21c7-039a-d4cb-202dbe0efc7d.png">

この記事ではこの問題の修正の仕方について学んでいきます。

## 問題を修正する

### スクロールを無効化する

デフォルトでは `UITextView` はスクロールが有効になっています。固定した高さにするためにそれを無効化する必要があります。

もともと、次のようなテーブルビューのクラスコードになっています。

```swift
class demoCell: UITableViewCell {
    @IBOutlet weak var textField: UITextView!
}
```

このコードを次のように更新します。

```swift
class demoCell: UITableViewCell {
    
    @IBOutlet weak var textField: UITextView!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        textField.isScrollEnabled = false
    }
    
}
```

再びシミュレーターでプログラムを実行すれば、`UITextView` のコンテンツが正常に表示されているのがわかります。

<img width="360" alt="Simulator Screen Shot - iPhone 11 - 2020-05-31 at 15.43.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/9a5ca104-702b-fa6f-1679-98ef8ff375cd.png">

### テキストの内容が変更された時にビューの高さを更新する。

`UITextView` ]内のテキストを編集しようとしても、テーブルビューのセルの高さは変わりません。しかし、高さは動的に変更する必要があります。

#### 自動で高さを設定する

テーブルビューのセルの高さを自動的に設定するようにシステムに指示することができます。

```swift
tableView.rowHeight = UITableView.automaticDimension
```

#### テキストの編集と連動させる

`UITextViewDelegate` を設定して、テキストが変更されたときにイベントが連動するようにします

```swift
class demoCell: UITableViewCell, UITextViewDelegate {
    
    @IBOutlet weak var textField: UITextView!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        textField.isScrollEnabled = false
        textField.delegate = self
    }
    
    func textViewDidChange(_ textView: UITextView) {
        //新しいテキスト
        
    }
    
}
```

#### テキスト編集をテーブルビューコントローラに報告する

この変更を `UITableViewController` に報告すると、`UITableViewController` がテーブルビューをリフレッシュしてセルの高さが更新されます。

プロトコルを作成する

```swift
protocol demoCellDelegate : AnyObject {
    func textDidChange()
}
```

テーブルビューのセルで、`demoCellDelegate.textDidChange()` の関数を呼び出します

```swift
class demoCell: UITableViewCell, UITextViewDelegate {
    
    @IBOutlet weak var textField: UITextView!
    
    weak var delegate: demoCellDelegate?
    
    override func awakeFromNib() {
        super.awakeFromNib()
        textField.isScrollEnabled = false
        textField.delegate = self
    }
    
    func textViewDidChange(_ textView: UITextView) {
        //新しいテキスト
        delegate?.textDidChange()
    }
    
}
```

そして、テーブルビューのクラスで、デリゲートを設定します:

```swift
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "demo") as! demoCell
    cell.textField.text = "テスト テスト テスト テスト テスト テスト テスト テスト テスト テスト テスト"
    cell.delegate = self
    return cell
}
```

```swift
extension ViewController: demoCellDelegate {
    
    func textDidChange() {
        //TODO
    }
    
}
```

#### テーブルをリロードせずにテーブルビューを更新する

ここで、テーブルの高さを更新するために、`reloadData()` を使用しないでください。代わりに `tableView.beginUpdates()` と `tableView.endUpdates()` を使用してください。

```swift
extension ViewController: demoCellDelegate {
    
    func textDidChange() {
        tableView.beginUpdates()
        tableView.endUpdates()
    }
    
}
```

そして、このプログラムをシミュレータで再度実行すると、次のようになります。

<img width="360" alt="ezgif-3-d9b71c7981f9.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/7ad2406f-b57b-a2eb-c2de-fe96a2e4c631.gif">

---

:relaxed: [Twitter @MszPro](https://twitter.com/MszPro)

:sunny: [私の公開されているQiita記事のリストをカテゴリー別にご覧いただけます。](http://mszpro.com/ioskiji/)
<img width="150" src="https://cdn.mszmagic.com/static-web-content/AppClipImage_small.png">