前言
原文链接:kukumalucn.github.io/blog/2018/1…
本文Demo地址:Demo ,喜欢请star
我的Demo集合:JXT_iOS_Demos ,喜欢请star
平时开发中,我们总会不时的和富文本打交道。有时,我们需要用富文本高亮某句话中的某个关键字,如果这个关键字只出现一次,或者说这句话比较短,用- (NSRange)rangeOfString:(NSString *)searchString
这个方法就可以很容易的确定这些关键字的range
,如果是固定的一句话,那甚至可以将range
写成固定值。但是如果是一篇文章,就会相对棘手些。 这个问题其实很像是关键字检索高亮的问题,网上有很多相关的算法甚至三方库,比如 ICTextView ,下面提供一个十分简单的基于系统API的方法来解决这个问题,效果如下:
Code
其实这个算法的核心就在于如何同时获取到字符串中重复出现的所有的子串的range
,- (NSRange)rangeOfString:(NSString *)searchString
方法只能获取到第一次出现的子串的range
,设置NSStringCompareOptions
,可以改变获取到的结果,但是也只能获取到一个,不知道系统有没有提供直接的方法,笔者暂时是没有发现…… 简单的算法就是遍历,网上也有很多人提供了代码,但大部分都是多次遍历,效率肯定不会太好,经过调试和优化,笔者有了如下方法,通过几万字的字符串匹配验证,依旧可以保持在毫秒级,应该是可以适用于大部分简单的场景了,主要是算法相对于正则或者其他方式,简单易懂:
- (void)jxt_enumerateRangeOfString:(NSString *)searchString usingBlock:(void (^)(NSRange searchStringRange, NSUInteger idx, BOOL *stop))block
{
if ([self isKindOfClass:[NSString class]] && self.length &&
[searchString isKindOfClass:[NSString class]] && searchString.length) {
NSArray <NSString *>*separatedArray = [self componentsSeparatedByString:searchString];
if (separatedArray.count < 2) {
return ;
}
NSUInteger count = separatedArray.count - 1; //少遍历一次,因为拆分之后,最后一部分是没用的
NSUInteger length = searchString.length;
__block NSUInteger location = 0;
[separatedArray enumerateObjectsUsingBlock:^(NSString * _Nonnull componentString, NSUInteger idx, BOOL * _Nonnull stop) {
if (idx == count) {
*stop = YES;
}
else {
location += componentString.length; //跳过待筛选串前面的串长度
if (block) {
block(NSMakeRange(location, length), idx, stop);
}
location += length; //跳过待筛选串的长度
}
}];
}
}
复制代码
具体使用如下:
[attributedString.string jxt_enumerateRangeOfString:searchString usingBlock:^(NSRange searchStringRange, NSUInteger idx, BOOL *stop) {
[attributedString addAttributes:@{
NSForegroundColorAttributeName:[UIColor redColor],
NSBackgroundColorAttributeName:[[UIColor blueColor] colorWithAlphaComponent:0.2],
} range:searchStringRange];
}];
复制代码
本文作者: 霖溦
本文链接: kukumalucn.github.io/blog/2018/1…
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明出处!