Question

I have a view which draws some text using coretext. Everything works fine, text shows right side up, flipping the coordinates. However, I must also respond to touches on specific runs, so I have the following code which gets called when a tap gesture recognizer fires:

- (void)receivedTap:(UITapGestureRecognizer*)recognizer
{
    CGPoint point = [recognizer locationInView:self];
    NSLog(@"point = %@", NSStringFromCGPoint(point));
    CGContextRef context = UIGraphicsGetCurrentContext();

    CFArrayRef lines = CTFrameGetLines(textFrame);
    CFIndex lineCount = CFArrayGetCount(lines);
    CGPoint origins[lineCount];

    CTFrameGetLineOrigins(textFrame, CFRangeMake(0, 0), origins);
    for(CFIndex idx = 0; idx < lineCount; idx++)
    {
        CTLineRef line = CFArrayGetValueAtIndex(lines, idx);
        CGRect lineBounds = CTLineGetImageBounds(line, context);
        lineBounds.origin.y += origins[idx].y;

        if(CGRectContainsPoint(lineBounds, point))
        {
            CFArrayRef runs = CTLineGetGlyphRuns(line);
            for(CFIndex j = 0; j < CFArrayGetCount(runs); j++)
            {
                CTRunRef run = CFArrayGetValueAtIndex(runs, j);
                NSDictionary* attributes = (NSDictionary*)CTRunGetAttributes(run);
                BOOL result = NO;
                NSURL* url = [attributes objectForKey:kJTextViewDataDetectorLinkKey];
                NSString* phoneNumber = [attributes objectForKey:kJTextViewDataDetectorPhoneNumberKey];
                NSDictionary* addressComponents = [attributes objectForKey:kJTextViewDataDetectorPhoneNumberKey];
                if(url)
                {
                    result = [[UIApplication sharedApplication] openURL:url];
                    return;
                }
                else if(phoneNumber)
                {
                    NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"tel://%@", phoneNumber]];
                    result = [[UIApplication sharedApplication] openURL:url];
                    return;
                }
                else if(addressComponents)
                {
                    NSMutableString* address = [NSMutableString string];
                    NSString* temp = nil;
                    if((temp = [addressComponents objectForKey:NSTextCheckingStreetKey]))
                        [address appendString:temp];
                    if((temp = [addressComponents objectForKey:NSTextCheckingCityKey]))
                        [address appendString:[NSString stringWithFormat:@"%@%@", ([address length] > 0) ? @", " : @"", temp]];
                    if((temp = [addressComponents objectForKey:NSTextCheckingStateKey]))
                        [address appendString:[NSString stringWithFormat:@"%@%@", ([address length] > 0) ? @", " : @"", temp]];
                    if((temp = [addressComponents objectForKey:NSTextCheckingZIPKey]))
                        [address appendString:[NSString stringWithFormat:@" %@", temp]];
                    if((temp = [addressComponents objectForKey:NSTextCheckingCountryKey]))
                        [address appendString:[NSString stringWithFormat:@"%@%@", ([address length] > 0) ? @", " : @"", temp]];
                    NSString* urlString = [NSString stringWithFormat:@"http://maps.google.com/maps?q=%@", [address stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
                    result = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
                    return;
                }
            }
        }
    }
}

Which works for the first line, if for instance, a web url which I'm doing some data detection over. I tap it, and Mobile Safari launches as I'd expect. However, this only works if the item is on the first line. I see the text coloured and underlined (for things like addresses and telephone numbers) on other lines, and I've verified the appropriate data is attached to my custom attributes in the string, but when I tap them, nothing occurs.

I know I have to somehow flip the rect lineBounds, but I'm not sure how to go about doing it. Any help would be greatly appreciated.

Was it helpful?

Solution

Actually what you need to do is iterate over lines in reverse. For instance, try substituting in this code:

NSArray* tempLines = (NSArray*)CTFrameGetLines(textFrame);
CFIndex lineCount = [tempLines count];//CFArrayGetCount(lines);
NSMutableArray* lines = [NSMutableArray arrayWithCapacity:lineCount];
for(id elem in [tempLines reverseObjectEnumerator])
    [lines addObject:elem];
CGPoint origins[lineCount];

OTHER TIPS

simplest way to reverse lines

        CFArrayRef lines = (CFArrayRef)[[(NSArray *)CTFrameGetLines(frame) reverseObjectEnumerator] allObjects];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top