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:
It is not a very good solution, but it works.. Please tell me if you know a better way.