iOS

可响应部分文字点击的Label

在不断填坑中前进。。

Posted by 三十一 on April 3, 2019

可响应部分文字点击的Label

平时开发中,几乎都会碰到一个需求,需要文本中部分文字能被点击。也有很多很好的第三方库的支持。本着学习的目的,写了一个简单的部分文字响应点击的UILabel 控件。

实现思路是绘制文本时,记录需要响应点击事件的文本位置,然后当点击控件时,根据触摸的点,计算出当前点击的点位于文本的位置。判断点击位置是否与需要响应点击的位置重合。

思路就是这样,主要就是两点:

  1. 记录响应点击的文本位置;
  2. 计算出触摸位置在文本中的位置。

记录响应范围

这里的实现是根据正则表达式来求出当前全部文本中所有符合条件的文本。所以可能有多个选项。

    NSArray* matches = [[NSRegularExpression regularExpressionWithPattern:self.sy_clickString options:NSRegularExpressionDotMatchesLineSeparators error:nil] matchesInString:string options:0 range:range];
    for(NSTextCheckingResult* match in matches) {
        [self formattWithRange:match.range attributedString:a_string];
        [selectRangeArray addObject:[NSValue valueWithRange:match.range]];
    }

还有一种是直接指定响应范围。

    // 如果外部设置的高亮区间不为空 设置高亮文本
    if (self.sy_clickRange.location != NSNotFound)
    {
        if (self.sy_clickRange.location >= 0 && self.sy_clickRange.location <= range.length && self.sy_clickRange.location + self.sy_clickRange.length <= range.length) {
            [self formattWithRange:self.sy_clickRange attributedString:a_string];
            [selectRangeArray addObject:[NSValue valueWithRange:self.sy_clickRange]];
        }else
        {
            NSAssert(NO, @"设置的可点击区间超出字符串最大区间");
        }
    }

计算点击位置所在文本

这里的实现是使用 NSLayoutManager 获取点击的点位于当前文本的位置。

- (NSInteger)characterIndexAtPoint:(CGPoint)location
{
    NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText];
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedText];
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    [textStorage addLayoutManager:layoutManager];
    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(CGRectGetWidth(self.bounds), CGFLOAT_MAX)];
    textContainer.maximumNumberOfLines = 100;
    textContainer.lineBreakMode = self.lineBreakMode;
    textContainer.lineFragmentPadding = 0.0;
    [layoutManager addTextContainer:textContainer];
    NSUInteger characterIndex = [layoutManager characterIndexForPoint:location
                                                      inTextContainer:textContainer
                             fractionOfDistanceBetweenInsertionPoints:NULL];
    return characterIndex;
}

Demo

已经上传至 github,项目地址:SYTouchLabel

可使用 CocoaPods 集成使用。

pod 'SYTouchLabel'

SYTouchLabel 使用方式:

    SYTouchLabel *label = [[SYTouchLabel alloc] init];
    label.lineBreakMode = NSLineBreakByCharWrapping;
    label.numberOfLines = 0;
    label.text = @"是的阿萨德法师是的阿萨德法师是的建设eeeeeeeeeeeeeeeeeeeeeeeeee社会主义新中国萨德法师是的阿萨德法师是的阿萨德法师是的阿萨德法师是的阿萨德法师";
    label.sy_clickString = @"eeeeeeeeeeeeeeeeeeeeeeeeee";
    label.sy_clickRange = NSMakeRange(2, 5 );
    [self.view addSubview:label];
    [label mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self.view);
        make.width.equalTo(self.view).offset(- 40);
    }];
    
    label.clickBlock = ^(NSString *clickString)
    {
        NSLog(@"点击的文本为 %@",clickString);
    };

SYTouchLabel 继承自 UILabel,除了设置 响应区域的方法和响应点击事件的方法,其他使用与原生的 UILabel 一样。核心代码就上面那么多,实现简单,功能也简单。

Demo