iOS Keyboard - input Accessory View xib with autolayout not receiving touch event
Asked Answered
S

2

7

I've built a growing UITextView attached to the keyboard, similar to the stock Messages app, by loading a .xib into the keyboard's inputAccessoryView as such:

self.keyboardAccessoryView = [[[NSBundle mainBundle]
                             loadNibNamed:@"KeyboardAccessoryView"
                             owner:self options:nil]
                             firstObject];

The .xib looks like this, and is using layout constraints so that the textView grows vertically when the user enters more lines of text:

enter image description here

This is all working great, with rotation and everything, except one big bug -- when the text is multiple lines, only the very bottom line handles touch events. This means that a user cannot scroll inside the UITextView because their touch events are being passed to the (dark gray) view in the back and scrolling that instead. They also cannot select and edit their text on the top 3 lines.

enter image description here

I think I could do a workaround by capturing the coordinates of all tap events and checking if the keyboard is open and how tall the UITextView is, then selecting the correct element to receive the touch event. But this is brittle solution that is more complicated with rotation. Is there something I'm missing in my auto-growing text view approach, or some easier fix?

Saxe answered 13/4, 2014 at 14:39 Comment(1)
I'm having trouble with getting the auto layout constraints to grow the accessory view vertically. I'm playing with layout Any chance you can share some insights there? I can only get the textview to grow (by changing its size constraint) while its container accessory view remains its original size.Neubauer
S
2

I figured out that even though the keyboard accessory input grows vertically with auto layout, its frame does not. So you have to adjust the keyboard accessory's frame each time the height of the uitextview grows, shrinks, and rotates. This introduces some complications as UITextView's in iOS7 are notoriously buggy -- I noticed behavior was not consistent across iPhone, iPad, and the Simulator.

Saxe answered 30/4, 2014 at 21:33 Comment(0)
M
7

To make input accessory view grow vertically you just set its autoresizingMask = .flexibleHeight, calculate its intrinsicContentSize and let the framework do the rest.

The code:

class InputAccessoryView: UIView, UITextViewDelegate {

    let textView = UITextView()

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

        // This is required to make the view grow vertically
        self.autoresizingMask = UIViewAutoresizing.flexibleHeight

        // Setup textView as needed
        self.addSubview(self.textView)
        self.textView.translatesAutoresizingMaskIntoConstraints = false
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[textView]|", options: [], metrics: nil, views: ["textView": self.textView]))
    self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[textView]|", options: [], metrics: nil, views: ["textView": self.textView]))

        self.textView.delegate = self

        // Disabling textView scrolling prevents some undesired effects,
        // like incorrect contentOffset when adding new line,
        // and makes the textView behave similar to Apple's Messages app
        self.textView.scrollEnabled = false
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override var intrinsicContentSize: CGSize {
        // Calculate intrinsicContentSize that will fit all the text
        let textSize = self.textView.sizeThatFits(CGSize(width: self.textView.bounds.width, height: CGFloat.max))
        return CGSize(width: self.bounds.width, height: textSize.height)
    }

    // MARK: UITextViewDelegate

    func textViewDidChange(_ textView: UITextView) {
        // Re-calculate intrinsicContentSize when text changes
        self.invalidateIntrinsicContentSize()
    }

}

This approach is quite straightforward and reliable, as it doesn't require hacking constraints or recreating the view each time its size changes.

Much answered 18/9, 2015 at 8:51 Comment(4)
i think this still passes through touch to the view behind the accessory view thoughSaxe
@Saxe I double checked and have no issues with touch events propagation when using this approach. Maybe your input accessory view setup differs somehow? Mine is based on this: robots.thoughtbot.com/input-accessorizing-uiviewcontrollerMuch
That is just amazing! Max, Thank you very much for sharing with us.Gabe
This is an incredible answer / view! Thank you @MuchInsurrectionary
S
2

I figured out that even though the keyboard accessory input grows vertically with auto layout, its frame does not. So you have to adjust the keyboard accessory's frame each time the height of the uitextview grows, shrinks, and rotates. This introduces some complications as UITextView's in iOS7 are notoriously buggy -- I noticed behavior was not consistent across iPhone, iPad, and the Simulator.

Saxe answered 30/4, 2014 at 21:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.