I have a text, which is displayed on a screen exactly like that:
#first SomePersonFirstName
SomePersonLastName #secondTag
In my application I keep information about 3 tags for this text together with their range, which are:
Now after I display it to user I want to detect which one was clicked, so I iterate through all informations I have, get a boundingRectForGlyphRange
for each of them, and check if this rect contains a point where it was clicked.
Everything works in general, but I got a strange situation which breaks my idea about how I do it. Problem is that bounding rects overlaps for those tags.
Because person tag was splitted into 2 lines, bound rects starts at 0,0 and covers #first bounding rect.
Problem is that when you click on this conflicted areas, there is no way to figure out which one was clicked.
I actually have no idea how to approach that. Is there any other way to detect bounding rect but that will not actually bound to empty areas, that will cover just text itself ?
EDIT
The code I'm using:
- (CGRect)boundingRectForCharacterRange:(NSRange)range
{
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:self.labelTitle.attributedText];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[textStorage addLayoutManager:layoutManager];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(self.labelTitle.bounds.size.width, CGFLOAT_MAX)];
textContainer.lineFragmentPadding = 0;
textContainer.lineBreakMode = self.labelTitle.lineBreakMode;
[layoutManager addTextContainer:textContainer];
NSRange glyphRange;
[layoutManager characterRangeForGlyphRange:range actualGlyphRange:&glyphRange];
return [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer];
}
-(void)tapped:(UIGestureRecognizer *)recognizer
{
UILabel *label = (UILabel *)recognizer.view;
CGPoint location = [recognizer locationInView:label];
CommentTag* selectedCommentTag = nil;
for (CommentTag* commentTag in self.comment.tags)
{
CGRect commentRect = [self boundingRectForCharacterRange:[commentTag tagRange]];
if(CGRectContainsPoint(commentRect, location))
{
if(selectedCommentTag == nil)
{
selectedCommentTag = commentTag;
}
else
{
if(selectedCommentTag.offset > commentTag.offset)
{
selectedCommentTag = commentTag;
}
}
}
}
if(selectedCommentTag)
{
if([selectedCommentTag.type isEqualToString:COMMENTTAG_TYPE_TAG])
{
NSLog(@"%@", [selectedCommentTag value]);
}
}
}
Here I just take the first tag to the left, but that doesn't solve a problem of #secondTag from my example
There are two (three) instances when two bounding rects can overlap:
For (1), you're just going to have to make your own set of rules for when you accept a click on a common glyph.
For (2), always prefer touches on the range that spans only one line -- you can tell this by comparing the rect height.
For (3), you'll have to iterate through the lines.
Alternatively, have you considered the opposite method?
NSRange characterRange = [layoutManager glyphRangeForBoundingRect:CGRectMake(point.x, point.y, 1, 1) inTextContainer:container];
if (NSLocationInRange(characterRange.location, firstRange) {
// ...
} else if (NSLocationInRange(characterRange.location, secondRange) {
// ...
} // etc...
By converting your touch point to a CGRect
with size 1x1
, you're able to get your layoutManager
to provide you an NSRange
of length 1
representing the single glyph at your touch point. Using this NSRange
's location
, you can compare to your NSRange
list and see which one you hit. Using your source:
- (NSUInteger)glyphIndexForPoint:(CGPoint)point
{
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:self.labelTitle.attributedText];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[textStorage addLayoutManager:layoutManager];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(self.labelTitle.bounds.size.width, CGFLOAT_MAX)];
textContainer.lineFragmentPadding = 0;
textContainer.lineBreakMode = self.labelTitle.lineBreakMode;
[layoutManager addTextContainer:textContainer];
return [layoutManager glyphIndexForPoint:point inTextContainer:textContainer];;
}
-(void)tapped:(UIGestureRecognizer *)recognizer
{
UILabel *label = (UILabel *)recognizer.view;
CGPoint location = [recognizer locationInView:label];
NSUInteger glyphIndex = [self glyphIndexForPoint:location];
CommentTag* selectedCommentTag = nil;
for (CommentTag* commentTag in self.comment.tags)
{
if (NSLocationInRange(glyphIndex, [commentTag tagRange]))
{
NSLog(@"%@", commentTag); //selected tag
break
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.