Ben Packard
Ben Packard

Reputation: 26526

UITextView providing incorrect caret rect

My UITextView delegate is logging the caret position using the following:

- (void)textViewDidBeginEditing:(UITextView *)textView
{
    CGPoint cursorPosition = [textView caretRectForPosition:textView.selectedTextRange.start].origin;
    NSLog(@"cursor: %@", NSStringFromCGPoint(cursorPosition));
}

But the reported position is always inaccurate. Specifically, it reports the previous cursor position - for example, if I click once inside the text view at position (x,y) then outside, then back inside at (x2,y2), on the second click the (x,y) coordinates are logged.

In fact, the selectedTextRange is the issue - the previous range is reported.

What am I missing? I don't see another delegate method I can use instead.

Upvotes: 7

Views: 3033

Answers (4)

Andrii Chernenko
Andrii Chernenko

Reputation: 10204

I had the same problem, but with UIKeyboardWillShowNotification. Adding a small delay helped in my case:

func keyboardWillAppear(notification: NSNotification!) {
    dispatch_after_delay(0.01) {            
        if textView.selectedTextRange {
             // at this point range is correct 
        }
    }
}

func dispatch_after_delay(delay:NSTimeInterval, queue: dispatch_queue_t = dispatch_get_main_queue(), block: dispatch_block_t) {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), queue, block)
}

Upvotes: 1

SFF
SFF

Reputation: 877

Actually, I found another solution to this.

You can use the UIKeyboardDidShowNotification to get the current selectedTextRange, and not the previous selectedTextRange. I hope it helps!

Upvotes: 1

Gandalf
Gandalf

Reputation: 2417

Value of selectedTextRange will be older only as the editing has just began, it has not changed yet. As you are calculating the cursorPostion with selected text, old values will lead to old position. So we need to calculate the new position inside a different delegate which reports after the selection changes. You will get correct readings with below mentioned code. Hope it helps

-(void)textViewDidChangeSelection:(UITextView *)textView
{
    CGPoint cursorPosition = [textView caretRectForPosition:textView.selectedTextRange.start].origin;
    NSLog(@"cursor start: %@", NSStringFromCGPoint(cursorPosition));
}

Upvotes: 5

Ben Packard
Ben Packard

Reputation: 26526

It's pretty gross but one solution is to introduce a short delay before reading calling the method:

UITextView *textView = note.object;

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    CGPoint cursorPosition = [textView caretRectForPosition:textView.selectedTextRange.start].origin;
    NSLog(@"cursor: %@", NSStringFromCGPoint(cursorPosition));
});

Upvotes: 4

Related Questions