Resize inputAccessoryView dynamically in iOS 8

SWIFT 5 Solution

If anyone has been trying to find for a solution in stackoverflow like me for input accessory view, I have made a simple project demo for a dynamically resizing input accessory with UITextView. It also comes with a tappable button on top of the UITextView to demonstrate added views on top of the Input Accessory View. All of these are loaded in a XIB for easy customisation. Written in Swift 5, and also takes care of the safe area for X devices.

class InputAccessoryView: UIView, UITextViewDelegate {

@IBOutlet weak var textView: UITextView!

@IBOutlet weak var accessoryButtonViewHeightConstraint: NSLayoutConstraint!

@IBOutlet weak var accessoryButtonView: UIView!
// MARK: - Init

required init?(coder aDecoder: NSCoder) {

    super.init(coder: aDecoder)

    self.setup()
}

override init(frame: CGRect) {

    super.init(frame: frame)

    self.setup()
}

private func setup() {

    let view = self.viewFromNibForClass()

    view?.frame = self.bounds

    view?.autoresizingMask = [.flexibleHeight, .flexibleWidth]

    self.accessoryButtonViewHeightConstraint.constant = 0

    if let view = view {

        self.addSubview(view)

        self.textView.translatesAutoresizingMaskIntoConstraints = false
    }
}

override func didMoveToWindow() {

    super.didMoveToWindow()

    if let window = self.window {

        self.bottomAnchor.constraint(lessThanOrEqualTo: window.safeAreaLayoutGuide.bottomAnchor).isActive = true
    }
}

private func viewFromNibForClass() -> UIView? {

    let bundle = Bundle(for: type(of: self))

    let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)

    let view = nib.instantiate(withOwner: self, options: nil).first as? UIView

    return view
}

func textViewDidChange(_ textView: UITextView) {

    self.accessoryButtonViewHeightConstraint.constant = 40

    self.accessoryButtonView.alpha = 1

    self.updateHeight(textView.contentSize.height + 20 + self.accessoryButtonViewHeightConstraint.constant)
}

private func updateHeight(_ height: CGFloat) {

    for constraint in self.constraints where constraint.firstAttribute == .height {

        constraint.constant = height
    }
}

@IBAction func accessoryButtonTouchUpInside(_ sender: Any) {

    let height = self.frame.size.height - 40

    self.updateHeight(height)

    self.accessoryButtonViewHeightConstraint.constant = 0
}
}

Here is an example of the usage: https://github.com/jaysalvador/InputAccessoryView


It works for me in iOS7/iOS8:

class MessageInputAccessory: UIView, UITextViewDelegate {
    private var textView: UITextView!
    private var heightConstraint: NSLayoutConstraint?

    override init(frame: CGRect) {
        super.init(frame: frame)

        commonInit()
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        commonInit()
    }

    func commonInit() {
        self.userInteractionEnabled = true

        textView = UITextView()
        textView.delegate = self
        textView.bounces = false
        textView.scrollEnabled = false
        textView.layer.cornerRadius = 5
        textView.layer.borderWidth = 1
        textView.layer.borderColor = UIColor.blueColor().CGColor

        self.addSubview(textView)
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        textView.frame = self.bounds
    }

    override func addConstraint(constraint: NSLayoutConstraint) {
        self.heightConstraint = constraint

        super.addConstraint(constraint)
    }

    func textViewDidChange(textView: UITextView) {
        var contentSize = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max))
        self.frame.size.height = contentSize.height

        if let heightConstraint = self.heightConstraint {
            heightConstraint.constant = self.frame.size.height
        }

        self.textView.reloadInputViews()
    }
}

EDIT: This works with xib too (iOS7/iOS8):

class MessageInputAccessory: UIView, UITextViewDelegate {
    @IBOutlet var textView: UITextView!
    @IBOutlet var textViewHeightConstraint: NSLayoutConstraint!
    private var heightConstraint: NSLayoutConstraint?

    override func awakeFromNib() {
        textView.layer.cornerRadius = 5
        textView.layer.borderWidth = 1
        textView.layer.borderColor = UIColor.blueColor().CGColor
    }

    override func layoutSubviews() {
        textViewHeightConstraint.constant = self.bounds.size.height

        super.layoutSubviews()
    }

    override func addConstraint(constraint: NSLayoutConstraint) {
        if constraint.firstItem === self {
            self.heightConstraint = constraint
        }

        super.addConstraint(constraint)
    }

    override func addConstraints(constraints: [AnyObject]) {
        super.addConstraints(constraints)
    }

    func textViewDidChange(textView: UITextView) {
        var contentSize = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max))
        self.frame.size.height = contentSize.height

        if let heightConstraint = self.heightConstraint {
            heightConstraint.constant = self.frame.size.height
        }

        self.textView.reloadInputViews()
    }

    override func intrinsicContentSize() -> CGSize {
        return self.bounds.size;
    }
}

There is my xib: enter image description here

It is not a very good solution, but it works.. Please tell me if you know a better way.