iOS
Swift
RxSwift

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

概要

イメージ

before after
before.gif after.gif

環境

  • Xcode 9.4
  • Swift 4.1
  • RxSwift 4.2
  • RxCocoa 4.2

サンプルリポジトリ

Viewを用意する

ViewController InputView
スクリーンショット 2018-08-19 21.35.03.png スクリーンショット 2018-08-19 21.32.11.png
  • ViewControllerの下部にInputViewを配置する

Constraintの設定

スクリーンショット 2018-08-19 21.36.59.png

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

viewc.gif

コード

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

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