Create UITextRange from NSRange Create UITextRange from NSRange objective-c objective-c

Create UITextRange from NSRange


You can create a text range with the method textRangeFromPosition:toPosition. This method requires two positions, so you need to compute the positions for the start and the end of your range. That is done with the method positionFromPosition:offset, which returns a position from another position and a character offset.

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


It is a bit ridiculous that seems to be so complicated. A simple "workaround" would be to select the range (accepts NSRange) and then read the selectedTextRange (returns UITextRange):

- (CGRect)frameOfTextRange:(NSRange)range inTextView:(UITextView *)textView {    textView.selectedRange = range;    UITextRange *textRange = [textView selectedTextRange];     CGRect rect = [textView firstRectForRange:textRange];    return rect;}

This worked for me even if the textView is not first responder.


If you don't want the selection to persist, you can either reset the selectedRange:

textView.selectedRange = NSMakeRange(0, 0);

...or save the current selection and restore it afterwards

NSRange oldRange = textView.selectedRange;// do something// then check if the range is still valid andtextView.selectedRange = oldRange;


Swift 4 of Andrew Schreiber's answer for easy copy/paste

extension NSRange {    func toTextRange(textInput:UITextInput) -> UITextRange? {        if let rangeStart = textInput.position(from: textInput.beginningOfDocument, offset: location),            let rangeEnd = textInput.position(from: rangeStart, offset: length) {            return textInput.textRange(from: rangeStart, to: rangeEnd)        }        return nil    }}