Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS. UITextView. Text jumping after UIKeyboardDidHideNotification

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.

like image 452
Nikita Mosyakov Avatar asked Oct 19 '22 20:10

Nikita Mosyakov


2 Answers

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 :)

like image 162
Jaymon Avatar answered Nov 15 '22 04:11

Jaymon


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.

like image 24
Henry Ngan Avatar answered Nov 15 '22 05:11

Henry Ngan