I have editable UITextView and keyboard dismiss mode is interactive. Also my controller is listening two notifications: UIKeyboardWillShowNotification, UIKeyboardWillHideNotification.
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
var insets = self.textView.contentInset;
let rect = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue() ?? CGRectZero
insets.bottom = (rect.size.height - (CGRectGetHeight(self.view.frame) - CGRectGetMaxY(self.textView.frame)))
self.textView.contentInset = insets
self.textView.scrollIndicatorInsets = insets
}
}
func keyboardWillHide(notification: NSNotification) {
self.textView.contentInset = UIEdgeInsetsZero
self.textView.scrollIndicatorInsets = UIEdgeInsetsZero
}
This stuff works great, if text in UITextView doesn't contain any empty lines. If it do, contentOffset jumps to another, random place.
I'm not sure if this is a bug in iOS 7+, or I am doing something wrong. If it's not a bug, how to get this going fluently without the jumping behaviour?
Thanks for your help.
I had been battling this exact same problem, when I would dismiss the keyboard the UITextView's content offset would jump back to {0, 0}. Interestingly, I only got this behavior on the device, but not in the simulator.
I originally tried to solve it by overriding UITextView's contentOffset method and having it just ignore {0, 0} values, and that was semi effective, until the content got too long, in which case it would just jump to a random offset, and set the same value 3 times (so it would set content offset to {0, 3605}, {0, 3605}, and {0, 3605} all in rapid succession).
After a long time spent looking for a solution, it turned out to be rather simple:
textview.layoutManager.allowsNonContiguousLayout = NO;
As discussed in this blog post. Hope that helps :)
I had 100% exactly the same problem as you and I also asked a question about it but no one could get it right. (I am the one who up voted and favourited your question!!)
I eventually did a workaround after 4 days of frustration. Just put the UITextView inside a UITableView (You don't need to put it inside a UITableViewCell, just drag to the UITableView then it's ok). Make your UITextView unscrollable.
The following method will make UITextView expand and update the UITableView every time it is changed. (Don't forget to connect UITextView's delegate)
func textViewDidChange(textView: UITextView) {
// Change textView height
self.textView.sizeToFit()
UIView.setAnimationsEnabled(false)
self.tableView.beginUpdates()
self.tableView.endUpdates()
UIView.setAnimationsEnabled(true)
}
The following method will make UITableView autoscroll to the cursor when UITextView becomes active.
func textViewDidBeginEditing(textView: UITextView) {
// Delay the following line so that it works properly
let delay = 0.005 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue()) {
var rect = self.textView.caretRectForPosition(self.textView.selectedTextRange?.end)
var changedRect = CGRectMake(rect.origin.x, rect.origin.y, rect.width, rect.height+3)
self.tableView.scrollRectToVisible(changedRect, animated: true)
}
}
You also need to change the UITableView contentInset and scrollIndicatorInsets in your keyboardWillShow and keyboardWillHide methods, depending on your screen layout.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With