4

I have a text view with a dynamic height. As the user adds or removes text the height of the text view changes.

My issue is that as the user adds text and the text view grows it disappears behind the keyboard. I have successfully moved the view when the keyboard appears so that the text view is hidden from the start but I can't seem to figure out how to keep it above the keyboard as the height changes. Any help is much appreciated!

Functions to move view when keyboard appears and disappears:

func keyboardWillShow(sender: NSNotification) {
    let info: NSDictionary = sender.userInfo!
    let value: NSValue = info.valueForKey(UIKeyboardFrameBeginUserInfoKey) as! NSValue
    let keyboardSize: CGSize = value.CGRectValue().size
    let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height + 20, 0.0)
    scrollView.contentInset = contentInsets

    var aRect: CGRect = self.view.frame
    aRect.size.height -= keyboardSize.height
    let activeTextFieldRect: CGRect? = activeItemRect()
    let activeTextFieldCentre: CGPoint? = CGPointMake(CGRectGetMidX(activeTextFieldRect!), CGRectGetMidY(activeTextFieldRect!))
    if (!CGRectContainsPoint(aRect, activeTextFieldCentre!)) {
        scrollView.scrollRectToVisible(activeTextFieldRect!, animated:true)
    }
}

func keyboardWillHide(sender: NSNotification) {
    let contentInsets: UIEdgeInsets = UIEdgeInsetsZero
    scrollView.contentInset = contentInsets
}
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
01Riv
  • 1,444
  • 1
  • 13
  • 28
  • What is your textView contained in? If it's a tableview, see http://stackoverflow.com/questions/18368567/uitableviewcell-with-uitextview-height-in-ios-7 – sschale May 26 '16 at 01:19
  • It is not a table view. The text field is a subview of the content view and the content view is a subview of a scroll view which is inside the original view. Thanks though! – 01Riv May 26 '16 at 01:21

1 Answers1

3

So what needs to happen is that the containing scrollView needs to be told to scroll whenever the location of the cursor changes, if it's outside of the bounds. I've adapted from the link a function to call to do this. Make sure to replace the scrollView with whatever yours is named.

internal func scrollToCursorForTextView(textView: UITextView) {
    var cursorRect = textView.caretRectForPosition(textView.selectedTextRange!.start)
    cursorRect = scrollView.convertRect(cursorRect, fromView: textView)
    if !self.rectVisible(cursorRect) {
      cursorRect.size.height += 8
      scrollView.scrollRectToVisible(cursorRect, animated: true)
    }
}

internal func rectVisible(rect: CGRect) -> Bool {
    var visibleRect = CGRect()
    visibleRect.origin = scrollView.contentOffset
    visibleRect.origin.y += scrollView.contentInset.top
    visibleRect.size = scrollView.bounds.size
    visibleRect.size.height -= scrollView.contentInset.top + scrollView.contentInset.bottom
    return CGRectContainsRect(visibleRect, rect)
}

Swift 4 Update

internal func scrollToCursorForTextView(textView: UITextView) {
    guard let startOfRange = textView.selectedTextRange?.start else { return }
    var cursorRect = textView.caretRect(for: startOfRange)
    cursorRect = scrollView.convert(cursorRect, from: textView)
    if !rectVisible(rect: cursorRect) {
        cursorRect.size.height += 8
        scrollView.scrollRectToVisible(cursorRect, animated: true)
    }
}

func rectVisible(rect: CGRect) -> Bool {
    var visibleRect = CGRect()
    visibleRect.origin = scrollView.contentOffset
    visibleRect.origin.y += scrollView.contentInset.top
    visibleRect.size = scrollView.bounds.size
    visibleRect.size.height -= scrollView.contentInset.top + scrollView.contentInset.bottom
    return visibleRect.contains(rect)
}
concertman
  • 13
  • 5
sschale
  • 5,168
  • 3
  • 29
  • 36
  • Thanks ill definitely try this out! But how are these functions called? Should I add a notification to listen for them? – 01Riv May 26 '16 at 01:46
  • You call them whenever the bounds of the textView are changed - you said that can happen dynamically. If you don't have a good method there, use `UITextFieldDelegate` methods. – sschale May 26 '16 at 03:03
  • 1
    I tried it in the method I used to change the text view height but nothing happened as well as in UITextViewDelegate methods and nothing happened there either. – 01Riv May 26 '16 at 03:09
  • Add some logging to see what's happening - I adapted this from use in a TableView. One other thing that may help you is to use https://github.com/michaeltyson/TPKeyboardAvoiding (make it the superclass of your ScrollView) which assists in automatically recalculating keyboard insets. – sschale May 26 '16 at 03:13
  • I get this in the logs every time a new line is added. Im not sure what it means. `requesting caretRectForPosition: while the NSTextStorage has oustanding changes {0, 1} ` the number in the x position increases every time a new line is added. – 01Riv May 26 '16 at 03:25