Character index at touch point for UILabel

后端 未结 7 1769
Happy的楠姐
Happy的楠姐 2020-12-05 03:09

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

7条回答
  •  温柔的废话
    2020-12-05 03:30

    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
    

提交回复
热议问题