3

I'm using this method for converting a NSRange to a CGRect as it relates to a UITextView:

- (CGRect)frameOfTextRange:(NSRange)range inTextView:(UITextView *)textView
{
    UITextPosition *beginning = textView.beginningOfDocument;
    UITextPosition *start = [textView positionFromPosition:beginning offset:range.location];
    UITextPosition *end = [textView positionFromPosition:beginning offset:range.location+range.length];
    UITextRange *textRange = [textView textRangeFromPosition:start toPosition:end];
    CGRect rect = [textView firstRectForRange:textRange];
    return rect;
}

This question's answer has someone asking the same question as me. The above method works great for this:

this is a great text field yes it is findthistoken

Not so great like this:

this is a great one
(new line)
(new line)
findthistoken

It seems to simply ignore the fact that there are 2 new lines characters? :/

How do I make it work with new line characters?

Community
  • 1
  • 1
Mike S
  • 4,092
  • 5
  • 35
  • 68

2 Answers2

11

You can force self.textView to ensure the layout of textView immediately with

 [self.textView.layoutManager ensureLayoutForTextContainer:self.textView.textContainer];

Placing that before your frameOfTextRange: call will gives you right rect.

Ravi Ojha
  • 1,480
  • 17
  • 27
Arshad
  • 906
  • 7
  • 12
  • oh WOW finally an answer :). I'll try it when I get home tonight! Its been really bugging me. – Mike S Sep 23 '14 at 03:08
1

For swift 5

In the case of a long textView with couple line breaks and where you have to scroll down, I've found that the problem still persisted even after adding the following line of code, before firstRectForRange:

[self.textView.layoutManager ensureLayoutForTextContainer:self.textView.textContainer];

firstRectForRange still returned a wrong frame, but a solution to this was to ensure layout after each scroll in scrollViewDidScroll.

For swift 5:

extension yourController {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        textView.layoutManager.ensureLayout(for:textView.textContainer)
    }
}
Abv
  • 354
  • 2
  • 13
  • Welcome Abv. This answer does not really answer the question. If @Arshad answer is not enough, explain precisely your point, and why you bring some scrolling code in the story. Thanks :) – Moose Mar 08 '21 at 16:25
  • 1
    Thank you @Moose. It is just that I have tried the solution of Arshad and it didn't work in the case of a textView with couple line breaks, scrolled down, even if I ensured layout just before the calculation of the frame. FirstRectForRange still returns a wrong frame in this case. The solution to this was to ensure the layout in scrollViewDidScroll. – Abv Mar 08 '21 at 16:43
  • Your edit clarifies and may help other in your situation. Well done. – Moose Mar 08 '21 at 17:55