UILabel 给指定字符添加点击事件

需要导入CoreText框架

效果图:

  • 日历控件点击2019:


    UILabel 给指定字符添加点击事件_第1张图片
    image.png
  • 点击2019打印的日志:


    UILabel 给指定字符添加点击事件_第2张图片
    image.png

1、给label增加tap手势:

UITapGestureRecognizer *yearTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapYearAction:)];
_montLabel.userInteractionEnabled = YES;
[_montLabel addGestureRecognizer:yearTap];

2、格式化label字符串:

NSString *yearString = [NSString stringWithFormat:@"%ld", [startDate year]];
[self formatDateLabelWithYearString:yearString withAllString:self.montLabel.text];

3、相关代码:

#pragma mark - 格式化日期,并给年份增加点击事件
- (void)formatDateLabelWithYearString:(NSString *)yearString withAllString:(NSString *)allString {
    if (yearString == nil || yearString.length <= 0 || allString == nil || allString.length < yearString.length) {
        return ;
    }
    NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:allString];
    NSRange yearRange = [allString rangeOfString:yearString];
    if (yearRange.location == NSNotFound) {
        return ;
    }
    [attr addAttribute:@"YSYearAttributeName" value:yearString range:yearRange];
    [attr addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:yearRange];
    self.montLabel.attributedText = attr;
}

- (void)tapYearAction:(UITapGestureRecognizer *)gesture {
    if (gesture.state == UIGestureRecognizerStateRecognized) {
        CGPoint point = [gesture locationInView:self.montLabel];
        NSDictionary *dic = [self textAttributesAtPoint:point withLabel:self.montLabel];
        for (NSString *attributeName in dic.allKeys) {
            if ([attributeName isEqualToString:@"YSYearAttributeName"]) {
                NSString *pointString = dic[@"YSYearAttributeName"];
                NSLog(@"你点击了 %@ 年", pointString);
            }
        }
    }
}

#pragma mark - UILabel中对应的点pt在UILabel中所有的字体属性(用coreText 实现)
- (NSDictionary*)textAttributesAtPoint:(CGPoint)pt withLabel:(UILabel*)lab{
    // Locate the attributes of the text within the label at the specified point
    NSDictionary* dictionary =nil;
    // First, create a CoreText framesetter
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)lab.attributedText);
    CGMutablePathRef framePath =CGPathCreateMutable();
    CGPathAddRect(framePath,NULL,CGRectMake(0,0, lab.frame.size.width, lab.frame.size.height));
    // Get the frame that will do the rendering.
    CFRange currentRange =CFRangeMake(0,0);
    CTFrameRef frameRef =CTFramesetterCreateFrame(framesetter, currentRange, framePath,NULL);
    CGPathRelease(framePath);
    // Get each of the typeset lines
    NSArray*lines = (__bridge id)CTFrameGetLines(frameRef);
    CFIndex linesCount = [lines count];
    CGPoint *lineOrigins = (CGPoint *)malloc(sizeof(CGPoint) * linesCount);
    CTFrameGetLineOrigins(frameRef,CFRangeMake(0, linesCount), lineOrigins);
    CTLineRef line = NULL;
    CGPoint lineOrigin = CGPointZero;
    // Correct each of the typeset lines (which have origin (0,0)) to the correct orientation (typesetting offsets from the bottom of the frame)
    CGFloat bottom = lab.frame.size.height;
    for (CFIndex i = 0; i < linesCount; ++i) {
        lineOrigins[i].y = lab.frame.size.height- lineOrigins[i].y;
        bottom = lineOrigins[i].y;
    }
    // Offset the touch point by the amount of space between the top of the label frame and the text
    pt.y -= (lab.frame.size.height - bottom) / 2;
    // Scan through each line to find the line containing the touch point y position
    for(CFIndex i = 0; i < linesCount; ++i) {
        line = (__bridge CTLineRef)[lines objectAtIndex:i];
        lineOrigin = lineOrigins[I];
        CGFloat descent, ascent;
        CGFloat width = CTLineGetTypographicBounds(line, &ascent, &descent,nil);
        if(pt.y < (floor(lineOrigin.y) + floor(descent))) {
            // Cater for text alignment set in the label itself (not in the attributed string)
            if(lab.textAlignment == NSTextAlignmentCenter) {
                pt.x -= (lab.frame.size.width - width) / 2;
            } else if (lab.textAlignment == NSTextAlignmentRight) {
                pt.x -= (lab.frame.size.width - width);
            }
            // Offset the touch position by the actual typeset line origin. pt is now the correct touch position with the line bounds
            pt.x -= lineOrigin.x;
            pt.y -= lineOrigin.y;
            // Find the text index within this line for the touch position
            CFIndex i =CTLineGetStringIndexForPosition(line, pt);
            // Iterate through each of the glyph runs to find the run containing the character index
            NSArray* glyphRuns = (__bridge id)CTLineGetGlyphRuns(line);
            CFIndex runCount = [glyphRuns count];
            for (CFIndex run = 0; run < runCount; ++ run) {
                CTRunRef glyphRun = (__bridge CTRunRef)[glyphRuns objectAtIndex:run];
                CFRange range = CTRunGetStringRange(glyphRun);
                if(i >= range.location && i<= range.location+range.length) {
                    dictionary = (__bridge NSDictionary*)CTRunGetAttributes(glyphRun);
                    break;
                }
            }
            if(dictionary) {
                break;
            }
        }
    }
    free(lineOrigins);
    CFRelease(frameRef);
    CFRelease(framesetter);
    return dictionary;
}

你可能感兴趣的:(UILabel 给指定字符添加点击事件)