12

I created a UITextView which is sized dynamically within a UITableViewCell as a person types.

On iOS 10 the UITextView's cursor is followed automatically with very little code needed.

In the UITextViewDelegate method textViewDidChange: I've used this code which updated the size of the text view without any jumping around.

func textViewDidChange(_ textView: UITextView) {
    UIView.setAnimationsEnabled(false)
    self.tableView.beginUpdates()
    self.tableView.endUpdates()
    UIView.setAnimationsEnabled(true)
    self.tableView.contentOffset = currentOffset
}

In iOS 11 this no longer works and when I type the cursor disappears under the keyboard. Note that I didn't need any changes when the keyboard appeared.

I know that there were changes in iOS 11 related to how content insets work but have been unable to figure out what changes I'd need to make to make this work.

Where should I be looking to make these changes to fix it?

-- Update --

It would turn out that removing all of that code solves my problem in iOS 11 and iOS 11 handles scrolling down to follow the cursor as I type automatically without any problems.

The one issue I have remaining is that I can only type up to 28 lines of text into the UITextView before it stops updating the UITextViews size.

arunjos007
  • 4,105
  • 1
  • 28
  • 43
Mark Reid
  • 2,611
  • 3
  • 23
  • 45
  • have you set the number of lines in this textView to be equal to zero? – Bruno Fulber Wide Oct 23 '17 at 19:27
  • can you provide a [minimal working example](https://stackoverflow.com/help/mcve)? – Thanh Pham Oct 24 '17 at 02:55
  • Have your tried with textView.textContainer.maximumNumberOfLines = 0. This will give no limit to number of lines – manismku Oct 24 '17 at 13:06
  • @BrunoFulberWide I didn't see that as an option in IB. – Mark Reid Oct 25 '17 at 00:29
  • @ThanhPham Besides the above code the rest is a UITableView with a UITextView in one of the cells. The UITextView is pinned to the cell sides with a greater than or equal height constraint. There is no other custom code than what you see above so there isn't anything else I have to provide. – Mark Reid Oct 25 '17 at 00:32
  • @manismku It's not so much that I can't get more lines. It's that the UITextView doesn't resize dynamically as the content of the text view changes. I will give your suggestion a try however. – Mark Reid Oct 25 '17 at 00:33

4 Answers4

8

I have the same problem, a UITableViewController with a UITableViewCell containing a non-scrolling editable UITextView in a cell, and the cursor would go behind the keyboard in iOS11. Worked fine in prior iOS versions.

I finally figured out a fix based on this and other articles:

- (void)textViewDidChange:(UITextView *)textView {
    // handle to UITableView
    UITableView *tableView = ....

    // tell table to resize itself
    [UIView performWithoutAnimation:^{
        [tableView beginUpdates];
        [tableView endUpdates];
    }];

    // get the caret rectangle so we can make sure we scroll it into view.
    CGRect unconvertedRect = [textView caretRectForPosition:textView.selectedTextRange.start];
    CGRect caretRect = [textView convertRect:unconvertedRect toView:tableView];
    // make the rect a little bigger so it's not at the extreme bottom of the view area
    caretRect.size.height += caretRect.size.height / 2;

    // this doesn't seem to work with a simple dispatch_async, but dispatch_after 0.1s seems to work.
    // why, i have no idea.
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [tableView scrollRectToVisible:caretRect animated:NO];
    });
}

Hope this helps someone...

rodfleischer
  • 320
  • 5
  • 9
3

From XCode 9, the calculation of row is automatic once you set properties in size inspector of tableview.Here, You can check this in image and I mad one change in func textViewDidChange(_ textView: UITextView) Below is my code

 func textViewDidChange(_ textView: UITextView) {
        UIView.setAnimationsEnabled(false)
        self.tableView.beginUpdates()
        self.tableView.endUpdates()
        UIView.setAnimationsEnabled(true)
        textView.sizeToFit()
    }

and It's working fine for me in iOS 11

Here is Image of my tableview from storyboard, you can see constraints as well, which are simple and I am using it without margins.

And your textview cursor issue, I would suggest you IQKeyboardManager, it will manage all things like this.

Dipak Kacha
  • 423
  • 2
  • 13
  • Thanks for this one. I gave it a try and its not working for me still. Could you share your constraints for auto layout with this set up please? – Mark Reid Oct 30 '17 at 14:09
  • To confirm, when I type in to the UITextView with this set up the cursor disappears under the keyboard so if you've got something to fix that can you share that also please? – Mark Reid Oct 30 '17 at 14:15
  • I have updated my answer, check it out. I hope it'll help you. – Dipak Kacha Oct 31 '17 at 05:46
1
class TextViewTableCell: UITableViewCell {

    @IBOutlet private weak var textView: UITextView! {
        didSet {
            textView.delegate = self
            textView.isScrollEnabled = false
        }
    }

    var textDidChangeHandler: (((text: String?, shouldScroll: Bool)) -> Void)?

    func textViewDidChange(_ textView: UITextView) {
        let size = textView.bounds.size
        let newSize = textView.sizeThatFits(CGSize(width: size.width, height: .greatestFiniteMagnitude))

        let shouldScroll = size.height != newSize.height ? textView.text.count == textView.selectedRange.location : false
        textDidChangeHandler?((text: textView.text, shouldScroll: shouldScroll))
    }
}

Inside you data source cellForRow method:

// dequeue TextViewTableCell cell
cell.textDidChangeHandler = { [weak self] (text, shouldScroll) in
    guard let this = self else { return }
    // do whatever actions with text if needed
    guard shouldScroll else { return }
    UIView.performWithoutAnimation {
        tableView.beginUpdates()
        tableView.endUpdates() 
    }
    tableView.scrollToRow(at: indexPath, at: .bottom, animated: false)
}

Also good to have minimum height constraint for text view (for e.g. >=44)

Maksym Musiienko
  • 1,248
  • 8
  • 16
  • I have tried this and didn't have any luck with it. I see some scrolling happening but once the text goes below the keyboard level the tableview scrolls down a little and then bounces back to the top. – Mark Reid Oct 31 '17 at 11:24
  • @MarkReid well, maybe in this case you're settings your tableView contentInset to wrong value? My example is related to UITableViewController, which handles tableView insets by default. – Maksym Musiienko Oct 31 '17 at 14:52
  • Thanks for the suggestion, however I too am just using UITableViewController unmodified for this specific case. – Mark Reid Nov 02 '17 at 09:59
0

I am using the sabe method to update a textView, and it's working fine for more than 28 lines of text, make sure you have all this properties set:

textView.showsVerticalScrollIndicator = false
textView.isScrollEnabled = false

also when i define the textView text i call textView.sizeToFit()

  • Hi, these are all set in my app currently and I'm still getting the same issue, but thank you for the response. – Mark Reid Oct 30 '17 at 14:09