0
votes

I need to be able to reliably get the index of the tapped character in a Core Text frame. I have this code, which reliably gets the correct line, but which is off from zero to 2 characters on the tapped index. The error is always forward; that is, it gets the index 1 to 2 characters in advance of the correct index. Toward the beginning of a long paragraph, it is usually off by 2 characters; toward the end, it is usually correct. My text is Greek text in an attributed string, not sure if that makes a difference.

Any help would be greatly appreciated.

Thanks.

- (void)receivedTap:(UITapGestureRecognizer*)recognizer
{
    CGAffineTransform transform = CGAffineTransformMake(1, 0, 0, -1, 0, self.bounds.size.height);
    CGPoint point = CGPointApplyAffineTransform([recognizer locationInView:self], transform);

    // Getting all CTLines
    CFArrayRef lines = CTFrameGetLines(textFrame);
    CFIndex countOfLines = CFArrayGetCount(lines);

    int lineNumber = 0;
    for ( CFIndex i = 0; i < countOfLines; i++ ) // For each CTLine
    {
        CTLineRef line = CFArrayGetValueAtIndex(lines, i);
        CGRect lineRect = CGRectZero;
        CGFloat ascent = 0, descent = 0;

        // Calculating CTLine frame 
        CTFrameGetLineOrigins(textFrame, CFRangeMake(i, 1), &lineRect.origin);
        lineRect.size.width = CTLineGetTypographicBounds(line, &ascent, &descent, NULL);
        lineRect.size.width -= CTLineGetTrailingWhitespaceWidth(line);
        lineRect.size.height = ascent + descent;
        lineRect.origin.y -= descent;
        lineRect = CGRectIntegral(lineRect);

        if ( ! CGRectContainsPoint(lineRect, point) ) {
            lineNumber++;
            continue; 
        }
        NSLog(@"line is %d", lineNumber);
        CFIndex touchedIndex = CTLineGetStringIndexForPosition(line, point);
        NSInteger touchedIndexInt = (NSInteger)touchedIndex;
        NSLog(@"found it : char %d", touchedIndexInt);
        break;
}
1
It appears that this method works best with justified text rather than left aligned text. Also, an ident at the beginning of the paragraph throws it off for the first line. Is there a reliable way of doing this? - Casey Perkins

1 Answers

1
votes

Have you tried placing it in UITextView and doing something like

    UITextPosition *touchPosition = [self.textView closestPositionToPoint:point];
    UITextRange *touchRange = [self.textView.tokenizer rangeEnclosingPosition:touchPosition withGranularity:UITextGranularityCharacter inDirection:UITextLayoutDirectionRight];
    NSLog(@"%@",[self.textView textInRange: touchRange]);