For a UILabel
, I\'d like to find out which character index is at specific point received from a touch event. I\'d like to solve this problem for iOS 7 using Tex
Here you are my implementation for the same problem. I have needed to mark #hashtags
and @usernames
with reaction on the taps.
I do not override drawTextInRect:(CGRect)rect
because default method works perfect.
Also I have found the following nice implementation https://github.com/Krelborn/KILabel. I used some ideas from this sample too.
@protocol EmbeddedLabelDelegate
- (void)embeddedLabelDidGetTap:(EmbeddedLabel *)embeddedLabel;
- (void)embeddedLabel:(EmbeddedLabel *)embeddedLabel didGetTapOnHashText:(NSString *)hashStr;
- (void)embeddedLabel:(EmbeddedLabel *)embeddedLabel didGetTapOnUserText:(NSString *)userNameStr;
@end
@interface EmbeddedLabel : UILabel
@property (nonatomic, weak) id delegate;
- (void)setText:(NSString *)text;
@end
#define kEmbeddedLabelHashtagStyle @"hashtagStyle"
#define kEmbeddedLabelUsernameStyle @"usernameStyle"
typedef enum {
kEmbeddedLabelStateNormal = 0,
kEmbeddedLabelStateHashtag,
kEmbeddedLabelStateUsename
} EmbeddedLabelState;
@interface EmbeddedLabel ()
@property (nonatomic, strong) NSLayoutManager *layoutManager;
@property (nonatomic, strong) NSTextStorage *textStorage;
@property (nonatomic, weak) NSTextContainer *textContainer;
@end
@implementation EmbeddedLabel
- (void)dealloc
{
_delegate = nil;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self setupTextSystem];
}
return self;
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self setupTextSystem];
}
- (void)setupTextSystem
{
self.userInteractionEnabled = YES;
self.numberOfLines = 0;
self.lineBreakMode = NSLineBreakByWordWrapping;
self.layoutManager = [NSLayoutManager new];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:self.bounds.size];
textContainer.lineFragmentPadding = 0;
textContainer.maximumNumberOfLines = self.numberOfLines;
textContainer.lineBreakMode = self.lineBreakMode;
textContainer.layoutManager = self.layoutManager;
[self.layoutManager addTextContainer:textContainer];
self.textStorage = [NSTextStorage new];
[self.textStorage addLayoutManager:self.layoutManager];
}
- (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
self.textContainer.size = self.bounds.size;
}
- (void)setBounds:(CGRect)bounds
{
[super setBounds:bounds];
self.textContainer.size = self.bounds.size;
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.textContainer.size = self.bounds.size;
}
- (void)setText:(NSString *)text
{
[super setText:nil];
self.attributedText = [self attributedTextWithText:text];
self.textStorage.attributedString = self.attributedText;
[self.gestureRecognizers enumerateObjectsUsingBlock:^(UIGestureRecognizer *recognizer, NSUInteger idx, BOOL *stop) {
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) [self removeGestureRecognizer:recognizer];
}];
[self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(embeddedTextClicked:)]];
}
- (NSMutableAttributedString *)attributedTextWithText:(NSString *)text
{
NSMutableParagraphStyle *style = [NSMutableParagraphStyle new];
style.alignment = self.textAlignment;
style.lineBreakMode = self.lineBreakMode;
NSDictionary *hashStyle = @{ NSFontAttributeName : [UIFont boldSystemFontOfSize:[self.font pointSize]],
NSForegroundColorAttributeName : (self.highlightedTextColor ?: (self.textColor ?: [UIColor darkTextColor])),
NSParagraphStyleAttributeName : style,
kEmbeddedLabelHashtagStyle : @(YES) };
NSDictionary *nameStyle = @{ NSFontAttributeName : [UIFont boldSystemFontOfSize:[self.font pointSize]],
NSForegroundColorAttributeName : (self.highlightedTextColor ?: (self.textColor ?: [UIColor darkTextColor])),
NSParagraphStyleAttributeName : style,
kEmbeddedLabelUsernameStyle : @(YES) };
NSDictionary *normalStyle = @{ NSFontAttributeName : self.font,
NSForegroundColorAttributeName : (self.textColor ?: [UIColor darkTextColor]),
NSParagraphStyleAttributeName : style };
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:@"" attributes:normalStyle];
NSCharacterSet *charSet = [NSCharacterSet characterSetWithCharactersInString:kWhiteSpaceCharacterSet];
NSMutableString *token = [NSMutableString string];
NSInteger length = text.length;
EmbeddedLabelState state = kEmbeddedLabelStateNormal;
for (NSInteger index = 0; index < length; index++)
{
unichar sign = [text characterAtIndex:index];
if ([charSet characterIsMember:sign] && state)
{
[attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:token attributes:state == kEmbeddedLabelStateHashtag ? hashStyle : nameStyle]];
state = kEmbeddedLabelStateNormal;
[token setString:[NSString stringWithCharacters:&sign length:1]];
}
else if (sign == '#' || sign == '@')
{
[attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:token attributes:normalStyle]];
state = sign == '#' ? kEmbeddedLabelStateHashtag : kEmbeddedLabelStateUsename;
[token setString:[NSString stringWithCharacters:&sign length:1]];
}
else
{
[token appendString:[NSString stringWithCharacters:&sign length:1]];
}
}
[attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:token attributes:state ? (state == kEmbeddedLabelStateHashtag ? hashStyle : nameStyle) : normalStyle]];
return attributedText;
}
- (void)embeddedTextClicked:(UIGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateEnded)
{
CGPoint location = [recognizer locationInView:self];
NSUInteger characterIndex = [self.layoutManager characterIndexForPoint:location
inTextContainer:self.textContainer
fractionOfDistanceBetweenInsertionPoints:NULL];
if (characterIndex < self.textStorage.length)
{
NSRange range;
NSDictionary *attributes = [self.textStorage attributesAtIndex:characterIndex effectiveRange:&range];
if ([attributes objectForKey:kEmbeddedLabelHashtagStyle])
{
NSString *value = [self.attributedText.string substringWithRange:range];
[self.delegate embeddedLabel:self didGetTapOnHashText:[value stringByReplacingOccurrencesOfString:@"#" withString:@""]];
}
else if ([attributes objectForKey:kEmbeddedLabelUsernameStyle])
{
NSString *value = [self.attributedText.string substringWithRange:range];
[self.delegate embeddedLabel:self didGetTapOnUserText:[value stringByReplacingOccurrencesOfString:@"@" withString:@""]];
}
else
{
[self.delegate embeddedLabelDidGetTap:self];
}
}
else
{
[self.delegate embeddedLabelDidGetTap:self];
}
}
}
@end