Search code examples
iosswiftheightinputaccessoryviewuiinputviewcontroller

inputAccessoryViewController height modification


I'm trying to use inputAccessoryViewController in my app, but faced a problem with changing height of accessory view. I tried to change frame/bounds of the view, I also tried to handle height of the accessory view using constraints. But nothing worked well.

InputViewController code:

import UIKit
import RxSwift
import RxCocoa


class InputViewController: UIInputViewController {
    private var separatorView: UIView?
    private var answerTextView: ConstrainedTextView?
    private var closeButton: UIButton?
    private var tipLabel: UILabel?

//    private var generalHeightConstraint: NSLayoutConstraint?
    private var separatorHeightConstraint: NSLayoutConstraint?
    private var answerTextViewBottomConstraint: NSLayoutConstraint?

    private let junk = DisposeBag()


    override func viewDidLoad() {
        super.viewDidLoad()

        configureView()
    }

    private func configureView() {

//        view.autoresizingMask = .flexibleHeight

        view.backgroundColor = UIColor.white
        view.frame = CGRect(x: 0, y: 0, width: view.superview?.bounds.width ?? 100, height: 70)
//        view.translatesAutoresizingMaskIntoConstraints = false
//        generalHeightConstraint = AutoLayoutSetAttribute(view, .height, 70)

        // Separator

        separatorView = UIView()
        separatorView?.backgroundColor = UIColor.gray
        separatorView?.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(separatorView!)
        AutoLayoutEqualizeSuper(separatorView, .left, 0)
        AutoLayoutEqualizeSuper(separatorView, .right, 0)
        AutoLayoutEqualizeSuper(separatorView, .top, 0)
        separatorHeightConstraint = AutoLayoutSetAttribute(separatorView, .height, 1)

        // Close Button

        closeButton = UIButton(type: .system)
        closeButton?.setTitle("Hide", for: .normal)
        closeButton?.titleLabel?.font = UIFont.systemFont(ofSize: 17)
        closeButton?.translatesAutoresizingMaskIntoConstraints = false
        closeButton?.addTarget(self, action: #selector(dismissKeyboard), for: .touchUpInside)
        view.addSubview(closeButton!)
        AutoLayoutSetAttribute(closeButton, .width, 70)
        AutoLayoutEqualizeSuper(closeButton, .right, -5)
        view.addConstraint(NSLayoutConstraint(item: closeButton!, attribute: .top, relatedBy: .equal, toItem: separatorView, attribute: .bottom, multiplier: 1, constant: 0))

        // Tip Label

        tipLabel = UILabel()
        tipLabel?.textColor = UIColor.darkGray
        tipLabel?.text = "Input answer:"
        tipLabel?.font = UIFont.systemFont(ofSize: 17)
        tipLabel?.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tipLabel!)
        AutoLayoutEqualizeSuper(tipLabel, .left, 5)
        AutoLayoutEqualize(tipLabel, separatorView, .top, 0)
        view.addConstraint(NSLayoutConstraint(item: tipLabel!, attribute: .right, relatedBy: .equal, toItem: closeButton, attribute: .left, multiplier: 1, constant: 0))

        // Text View

        answerTextView = ConstrainedTextView()
        answerTextView?.backgroundColor = UIColor.white
        answerTextView?.delegate = self
        answerTextView?.scrollsToTop = false
        answerTextView?.showsVerticalScrollIndicator = false
        answerTextView?.font = REG_FONT(15)
        answerTextView?.maxLines = 5
        answerTextView?.translatesAutoresizingMaskIntoConstraints = false

        answerTextView?.layer.masksToBounds = true
        answerTextView?.layer.cornerRadius = 7

        answerTextView?.layer.borderColor = UIColor.lightGray.withAlphaComponent(0.7).cgColor
        answerTextView?.layer.borderWidth = 1

        view.addSubview(answerTextView!)
        AutoLayoutEqualizeSuper(answerTextView, .left, 5)
        AutoLayoutEqualizeSuper(answerTextView, .right, -5)
        answerTextViewBottomConstraint = AutoLayoutEqualizeSuper(answerTextView, .bottom, -5)
        view.addConstraint(NSLayoutConstraint(item: answerTextView!, attribute: .top, relatedBy: .equal, toItem: tipLabel, attribute: .bottom, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: answerTextView!, attribute: .top, relatedBy: .equal, toItem: closeButton, attribute: .bottom, multiplier: 1, constant: 0))

        answerTextView?
            .rx
            .observe(CGRect.self, "bounds")
            .distinctUntilChanged {
                $0?.size.height == $1?.size.height
            }
            .subscribe { [unowned self] newBounds in
                if var newHeight = newBounds.element??.size.height,
                    let separatorHeight = self.separatorHeightConstraint?.constant,
                    let buttonHeight = self.closeButton?.intrinsicContentSize.height,
                    let bottomSpace = self.answerTextViewBottomConstraint?.constant {

                    newHeight = newHeight == 0 ? 30 : newHeight

                    let generalHeight = newHeight + separatorHeight + buttonHeight + abs(bottomSpace)

                    self.view.frame = CGRect(x: 0, y: 0, width: self.view.superview?.bounds.width ?? 100, height: generalHeight)
//                    self.generalHeightConstraint?.constant = generalHeight

//                    UIView.animate(withDuration: 0.2) {
//                        self.view.setNeedsLayout()
//                        self.view.layoutIfNeeded()
//                    }
                }
            }
            .addDisposableTo(junk)
    }
}


// MARK: - UITextViewDelegate Protocol Conformance

extension InputViewController: UITextViewDelegate {

    func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
        textView.inputAccessoryView = view
        return true
    }

    func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
        textView.inputAccessoryView = nil
        return true
    }

}

View Controller where input accessory VC is used:

import UIKit

class TestViewController: UIViewController {
    override var inputAccessoryViewController: UIInputViewController? {
        return SDAnswerInputViewController()
    }

    override var canBecomeFirstResponder: Bool {
        return true
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

Can you explain how shall I correctly modify height of input accessory view overriding inputAccessoryViewController?


Solution

  • The problem was in these two lines:

    view.addConstraint(NSLayoutConstraint(item: answerTextView!, attribute: .top, relatedBy: .equal, toItem: tipLabel, attribute: .bottom, multiplier: 1, constant: 0))
    view.addConstraint(NSLayoutConstraint(item: answerTextView!, attribute: .top, relatedBy: .equal, toItem: closeButton, attribute: .bottom, multiplier: 1, constant: 0))
    

    The answerTextView couldn't modify it's height because of constraints at the bottom and the top.