How can I get the selected text frame from a UITextView

I think [UITextInput selectedTextRange] and [UITextInput caretRectForPosition:] is what you are looking for.

[UITextInput selectedTextRange] returns the selected range in character

[UITextInput caretRectForPosition:] returns the CGRect of the character range in this input.

UITextView conforms to UITextInput (since iOS 5), so you can use these methods for your UITextView instance.

It is going to be something like this.

UITextRange * selectionRange = [textView selectedTextRange];
CGRect selectionStartRect = [textView caretRectForPosition:selectionRange.start];
CGRect selectionEndRect = [textView caretRectForPosition:selectionRange.end];
CGPoint selectionCenterPoint = (CGPoint){(selectionStartRect.origin.x + selectionEndRect.origin.x)/2,(selectionStartRect.origin.y + selectionStartRect.size.height / 2)};

EDIT : Since the sample code became a little hard to get, I added an image for complementing.

An image that illustrates what local variables represent


Swift 5 version:

if let selectionRect = textView.selectedTextRange {
        let selectionRects = textView.selectionRects(for: selectionRect)
        var completeRect = CGRect.null
        for rect in selectionRects {
            if (completeRect.isNull) {
                completeRect = rect.rect
            } else {
                completeRect = rect.rect.union(completeRect)
            }
        }
    }

There are some situations where barley's answer won't actually give the center of the selection. For example:

Screenshot showing an example of where using the caret rects would not be accurate

In this case you can see the Copy/Paste menu is displaying in the center of the selection, which spans the entire width of the text field. But calculating the center of the two caret rects would give a position much further to the right.

You can get a more precise result using selectionRectsForRange:

UITextRange *selectionRange = [textView selectedTextRange];
NSArray *selectionRects = [self.textView selectionRectsForRange:selectionRange];
CGRect completeRect = CGRectNull;
for (UITextSelectionRect *selectionRect in selectionRects) {
    if (CGRectIsNull(completeRect)) {
        completeRect = selectionRect.rect;
    } else completeRect = CGRectUnion(completeRect,selectionRect.rect);
}

It's also worth clarifying that if you're still supporting iOS 4 and using either of these answers, you'll need to make sure these methods are supported before calling them: if ([textView respondsToSelector:@selector(selectedTextRange)]) { …