Dmitriy Stupivtsev Dmitriy Stupivtsev - 1 month ago 18
Swift Question

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?

Answer

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.