UITextViewへの入力文字を監視し、Viewの高さを動的に変更する with RxSwift

# 概要

- UITextViewへの文字入力を監視し、Viewの高さを変更させる
- 過去の記事([iMessageの入力UIのようなキーボードの表示と連動するUIを作る with RxSwift, RxKeyboard](https://qiita.com/k0uhashi/items/671ec40520967bc3949f))の開発の延長という体で書いてます


# イメージ

|before|after|
|:-:|:-:|
|![before.gif](https://qiita-image-store.s3.amazonaws.com/0/126441/ade43417-b5ee-09cd-d1d2-437f7bd2d8b2.gif)|![after.gif](https://qiita-image-store.s3.amazonaws.com/0/126441/983ee113-015c-3d1e-8267-675e1f6bb754.gif)|

# 環境

- Xcode 9.4
- Swift 4.1
- RxSwift 4.2
- RxCocoa 4.2

# サンプルリポジトリ

- [https://github.com/ryo-takahashi/LineInputUIExampleWithRxSwift](https://github.com/ryo-takahashi/LineInputUIExampleWithRxSwift)

# Viewを用意する

|ViewController|InputView|
|:-:|:-:|
|![スクリーンショット 2018-08-19 21.35.03.png](https://qiita-image-store.s3.amazonaws.com/0/126441/e6ea63ca-128c-cde3-2395-20e43af7dad7.png)|![スクリーンショット 2018-08-19 21.32.11.png](https://qiita-image-store.s3.amazonaws.com/0/126441/8f0d497d-5f47-fb98-ec0e-c5118f3b2da5.png)|

- ViewControllerの下部にInputViewを配置する

# Constraintの設定

![スクリーンショット 2018-08-19 21.36.59.png](https://qiita-image-store.s3.amazonaws.com/0/126441/025730f1-fe3a-4862-d28a-c87775915117.png)

- ViewControllerの下部に設定したInputViewに高さのConstraintをつける
- 実装では、以下のように入力文字に合わせてこのConstraintの値をいじっていくことで、動的に高さを変更させる

![viewc.gif](https://qiita-image-store.s3.amazonaws.com/0/126441/cb056dbe-cdcc-235e-1ee7-92757e097106.gif)

# コード

```swift:ViewController.swift
import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {

    @IBOutlet weak var messageInputView: MessageInputView!
    @IBOutlet weak var messageInputViewHeightConstraint: NSLayoutConstraint!
    
    private let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupMessageInputView()
    }

    private func setupMessageInputView() {
        let inputTextViewVerticalMargin: CGFloat = 12.0
        messageInputView.inputTextView.rx.text
            .asDriver()
            .drive(onNext: { [weak self] _ in
                // テキスト入力されるたびに呼ばれる　（削除でも）
                guard let height = self?.messageInputView.inputTextView.contentSize.height else { return }
                let inputTextViewHeight = height + inputTextViewVerticalMargin
                self?.messageInputViewHeightConstraint.constant = inputTextViewHeight
                self?.view.setNeedsLayout()
                self?.view.layoutIfNeeded()
            })
            .disposed(by: disposeBag)
    }

}
```

- 上記の実装で高さを動的に変更できるようになる
- しかし、高さの制限がないので下記のGIFのように画面の外までUIが伸びてしまう

![hei.gif](https://qiita-image-store.s3.amazonaws.com/0/126441/99ac66e5-848b-5858-ec7a-1ab262007fba.gif)

- 次に、高さの制限をする
    - といっても実装は簡単で、あるラインを決めてそれ以上の値になったら変更をさせないようにすればよい

```swift:ViewController.swift

        let inputTextViewMaxHeight: CGFloat = 100.0 // New Line
        let inputTextViewVerticalMargin: CGFloat = 12.0
        messageInputView.inputTextView.rx.text
            .asDriver()
            .drive(onNext: { [weak self] _ in
                guard let height = self?.messageInputView.inputTextView.contentSize.height else { return }
                let inputTextViewHeight = height + inputTextViewVerticalMargin
                if inputTextViewMaxHeight > inputTextViewHeight { // New Line
                    self?.messageInputViewHeightConstraint.constant = inputTextViewHeight
                } // New Line
                self?.view.setNeedsLayout()
                self?.view.layoutIfNeeded()
            })
            .disposed(by: disposeBag)
```

# Notes

- 高さの制限を変更したい場合は、 `inputTextViewMaxHeight` の値を変更しよう