I'd just like to add on Ramshad's answer, about how to deal with the valid frame.
For this you might want to consider using UITextView instead of UILabel, which doesn't give you access to how it manages layout the text. By disabling editing, selection and scrolling, UITextView behaves roughly the same as a UILabel, except some padding you have to remove.
For convenience, you might want to add a little category to UITextView, in which you write a method to test if a point touches any of the characters in range.
- (BOOL)point:(CGPoint)point touchesSomeCharacterInRange:(NSRange)range
{
NSRange glyphRange = [self.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL];
BOOL touches = NO;
for (NSUInteger index = glyphRange.location; index < glyphRange.location + glyphRange.length; index++) {
CGRect rectForGlyphInContainer = [self.layoutManager boundingRectForGlyphRange:NSMakeRange(index, 1) inTextContainer:self.textContainer];
CGRect rectForGlyphInTextView = CGRectOffset(rectForGlyphInContainer, self.textContainerInset.left, self.textContainerInset.top);
if (CGRectContainsPoint(rectForGlyphInTextView, point)) {
touches = YES;
break;
}
}
return touches;
}
This would also work for a fragment of text containing multiple words spreading across multiple lines due to word wrap. It would also work on localized texts as we deal with glyphs that are printed.