首先看下demo效果,下载地址git
有且只有
如下三大类总计 25 种匹配
汉语全拼拼音及每一个拼音字母所对应汉字的位置
和 汉语简拼拼音和每一个拼音字母对应汉字的位置
,将初始化以后的信息缓存起来+ (instancetype)personWithName:(NSString *)name hanyuPinyinOutputFormat:(HanyuPinyinOutputFormat *)pinyinFormat {
WPFPerson *person = [[WPFPerson alloc] init];
/** 将汉字转化为拼音的类方法 * name : 须要转换的汉字 * pinyinFormat : 拼音的格式化器 * @"" : seperator 分隔符 */
NSString *completeSpelling = [PinyinHelper toHanyuPinyinStringWithNSString:name withHanyuPinyinOutputFormat:pinyinFormat withNSString:@""];
// 首字母所组成的字符串
NSString *initialString = @"";
// 全拼拼音数组
NSMutableArray *completeSpellingArray = [[NSMutableArray alloc] init];
// 拼音首字母的位置数组
NSMutableArray *pinyinFirstLetterLocationArray = [[NSMutableArray alloc] init];
// 遍历每个字符
for (NSInteger x =0; x<name.length; x++) {
NSRange range = NSMakeRange(x, 1);
// 获取字符
NSString* hanyuCharString = [name substringWithRange:range];
// 若是该字符是中文
if ([WPFPinYinTools isChinese:hanyuCharString]) {
// 获取该字符的第一个拼音字母,如 wang 的 firstLetter 就是 w
NSString *firstLetter = [WPFPinYinTools firstCharactor:hanyuCharString withFormat:pinyinFormat];
// 获取该字符的拼音全拼,如 王 的 pinyinString就是 wang
NSString *pinyinString = [PinyinHelper toHanyuPinyinStringWithNSString:hanyuCharString withHanyuPinyinOutputFormat:pinyinFormat withNSString:@""];
/** 👉 👉获取该字符的拼音在整个字符串中的位置,如 "wang peng fei" 👈 👈 * 👉 👉 "wang" 对应的四个拼音字母是 0,0,0,0, 👈 👈 * 👉 👉 "peng" 对应的四个拼音字母是 1,1,1,1, 👈 👈 * 👉 👉 "fei" 对应的三个拼音字母是 2,2,2, 👈 👈 */
for (NSInteger j= 0 ;j<pinyinString.length ; j++) {
[completeSpellingArray addObject:@(x)];
}
// 拼接首字母字符串,如 "王鹏飞" 对应的首字母字符串就是 "wpf"
initialString = [initialString stringByAppendingString:firstLetter];
// 👉 👉 拼接首字母位置字符串,如 "王鹏飞" 对应的首字母位置就是 "0,1,2" 👈 👈
[pinyinFirstLetterLocationArray addObject:@(x)];
} else {
[completeSpellingArray addObject:@(x)];
[pinyinFirstLetterLocationArray addObject:@(x)];
initialString = [initialString stringByAppendingString:hanyuCharString];
}
}
person.name = name;
person.completeSpelling = completeSpelling;
person.initialString = initialString;
person.pinyinLocationString = [completeSpellingArray componentsJoinedByString:@","];
person.initialLocationString = [pinyinFirstLetterLocationArray componentsJoinedByString:@","];
return person;
}
复制代码
UISearchResultsUpdating
代理方法 - (void)updateSearchResultsForSearchController:(UISearchController *)searchController
来实时获取输入的最新关键字,并遍历数据源,将匹配到的结果显示出来// 更新搜索结果
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
NSLog(@"%@", searchController.searchBar.text);
[self.searchResultVC.resultDataSource removeAllObjects];
for (WPFPerson *person in self.dataSource) {
WPFSearchResultModel *resultModel = [WPFPinYinTools
searchEffectiveResultWithSearchString:searchController.searchBar.text.lowercaseString
nameString:person.name
completeSpelling:person.completeSpelling
initialString:person.initialString
pinyinLocationString:person.pinyinLocationString
initialLocationString:person.initialLocationString];
if (resultModel.highlightRang.length) {
person.highlightLoaction = resultModel.highlightRang.location;
person.textRange = resultModel.highlightRang;
person.matchType = resultModel.matchType;
[self.searchResultVC.resultDataSource addObject:person];
}
};
// 将匹配结果按照产品规则进行排序
[self.searchResultVC.resultDataSource sortUsingDescriptors:[WPFPinYinTools sortingRules]];
// 刷新tableView
dispatch_async(dispatch_get_main_queue(), ^{
[self.searchResultVC.tableView reloadData];
});
}
复制代码
+ (WPFSearchResultModel *)searchEffectiveResultWithSearchString:(NSString *)searchStrLower
nameString:(NSString *)nameStrLower
completeSpelling:(NSString *)completeSpelling
initialString:(NSString *)initialString
pinyinLocationString:(NSString *)pinyinLocationString
initialLocationString:(NSString *)initialLocationString {
WPFSearchResultModel *searchModel = [[WPFSearchResultModel alloc] init];
NSArray *completeSpellingArray = [pinyinLocationString componentsSeparatedByString:@","];
NSArray *pinyinFirstLetterLocationArray = [initialLocationString componentsSeparatedByString:@","];
// 彻底中文匹配范围
NSRange chineseRange = [nameStrLower rangeOfString:searchStrLower];
// 拼音全拼匹配范围
NSRange complateRange = [completeSpelling rangeOfString:searchStrLower];
// 拼音首字母匹配范围
NSRange initialRange = [initialString rangeOfString:searchStrLower];
// 汉字直接匹配
if (chineseRange.length!=0) {
searchModel.highlightedRange = chineseRange;
searchModel.matchType = MatchTypeChinese;
return searchModel;
}
NSRange highlightedRange = NSMakeRange(0, 0);
// MARK: 拼音全拼匹配
if (complateRange.length != 0) {
if (complateRange.location == 0) {
// 拼音首字母匹配从0开始,即搜索的关键字与该数据源第一个汉字匹配到,因此高亮范围从0开始
highlightedRange = NSMakeRange(0, [completeSpellingArray[complateRange.length-1] integerValue] +1);
} else {
/** 若是该拼音字符是一个汉字的首个字符,如搜索“g”, * 就要匹配出“gai”、“ge”等“g”开头的拼音对应的字符, * 而不该该匹配到“wang”、“feng”等非”g“开头的拼音对应的字符 */
NSInteger currentLocation = [completeSpellingArray[complateRange.location] integerValue];
NSInteger lastLocation = [completeSpellingArray[complateRange.location-1] integerValue];
if (currentLocation != lastLocation) {
// 高亮范围从匹配到的第一个关键字开始
highlightedRange = NSMakeRange(currentLocation, [completeSpellingArray[complateRange.length+complateRange.location -1] integerValue] - currentLocation +1);
}
}
searchModel.highlightedRange = highlightedRange;
searchModel.matchType = MatchTypeComplate;
if (highlightedRange.length!=0) {
return searchModel;
}
}
// MARK: 拼音首字母匹配
if (initialRange.length!=0) {
NSInteger currentLocation = [pinyinFirstLetterLocationArray[initialRange.location] integerValue];
NSInteger highlightedLength;
if (initialRange.location ==0) {
highlightedLength = [pinyinFirstLetterLocationArray[initialRange.length-1] integerValue]-currentLocation +1;
// 拼音首字母匹配从0开始,即搜索的关键字与该数据源第一个汉字匹配到,因此高亮范围从0开始
highlightedRange = NSMakeRange(0, highlightedLength);
} else {
highlightedLength = [pinyinFirstLetterLocationArray[initialRange.length+initialRange.location-1] integerValue]-currentLocation +1;
// 高亮范围从匹配到的第一个关键字开始
highlightedRange = NSMakeRange(currentLocation, highlightedLength);
}
searchModel.highlightedRange = highlightedRange;
searchModel.matchType = MatchTypeInitial;
if (highlightedRange.length!=0) {
return searchModel;
}
}
searchModel.highlightedRange = NSMakeRange(0, 0);
searchModel.matchType = NSIntegerMax;
return searchModel;
}
复制代码
首先筛选出一个比较全的第三方库 PinYin4Objc用于汉语转拼音,拼音的 unicode 库比较全,一些新的汉字也都能转成拼音github
可是因为该库很久没有更新,获取拼音文件部分代码不适合组件化的直接开发,所以我直接合到源文件里面了数组
汉语转拼音的格式缓存
// 获取格式化器
+ (HanyuPinyinOutputFormat *)getOutputFormat {
HanyuPinyinOutputFormat *pinyinFormat = [[HanyuPinyinOutputFormat alloc] init];
/** 设置大小写 * CaseTypeLowercase : 小写 * CaseTypeUppercase : 大写 */
[pinyinFormat setCaseType:CaseTypeLowercase];
/** 声调格式 :如 王鹏飞 * ToneTypeWithToneNumber : 用数字表示声调 wang2 peng2 fei1 * ToneTypeWithoutTone : 无声调表示 wang peng fei * ToneTypeWithToneMark : 用字符表示声调 wáng péng fēi */
[pinyinFormat setToneType:ToneTypeWithoutTone];
/** 设置特殊拼音ü的显示格式: * VCharTypeWithUAndColon : 以U和一个冒号表示该拼音,例如:lu: * VCharTypeWithV : 以V表示该字符,例如:lv * VCharTypeWithUUnicode : 以ü表示 */
[pinyinFormat setVCharType:VCharTypeWithV];
return pinyinFormat;
}
复制代码
+ (NSArray *)sortingRules {
// 按照 matchType 顺序排列,即优先展现 中文,其次是全拼匹配,最后是拼音首字母匹配
NSSortDescriptor *desType = [NSSortDescriptor sortDescriptorWithKey:@"matchType" ascending:YES];
// 优先显示 高亮位置索引靠前的搜索结果
NSSortDescriptor *desLocation = [NSSortDescriptor sortDescriptorWithKey:@"highlightLoaction" ascending:YES];
return @[desType,desLocation];
}
复制代码
####四. 循环方法测试及优化选择过程服务器
在优化遍历方法的过程当中,测试了几种遍历方法,这里以输入关键字“wang”为测试数据,测试真机机型为iPhone SE 10.3
微信
/** 2017-12-06 12:02:51.943006 HighlightedSearch[4459:1871193] w 2017-12-06 12:02:51.943431 HighlightedSearch[4459:1871193] 开始匹配,开始时间:2017-12-06 04:02:51 +0000 2017-12-06 12:02:51.980588 HighlightedSearch[4459:1871193] 匹配结束,结束时间:2017-12-06 04:02:51 +0000,耗时:0.0372 2017-12-06 12:02:52.284488 HighlightedSearch[4459:1871193] wa 2017-12-06 12:02:52.284771 HighlightedSearch[4459:1871193] 开始匹配,开始时间:2017-12-06 04:02:52 +0000 2017-12-06 12:02:52.316536 HighlightedSearch[4459:1871193] 匹配结束,结束时间:2017-12-06 04:02:52 +0000,耗时:0.0318 2017-12-06 12:02:52.516826 HighlightedSearch[4459:1871193] wan 2017-12-06 12:02:52.517121 HighlightedSearch[4459:1871193] 开始匹配,开始时间:2017-12-06 04:02:52 +0000 2017-12-06 12:02:52.545542 HighlightedSearch[4459:1871193] 匹配结束,结束时间:2017-12-06 04:02:52 +0000,耗时:0.0285 2017-12-06 12:02:52.838220 HighlightedSearch[4459:1871193] wang 2017-12-06 12:02:52.838602 HighlightedSearch[4459:1871193] 开始匹配,开始时间:2017-12-06 04:02:52 +0000 2017-12-06 12:02:52.880200 HighlightedSearch[4459:1871193] 匹配结束,结束时间:2017-12-06 04:02:52 +0000,耗时:0.0417 */
for (NSInteger i = 0; i < self.dataSource.count; i++) {
复制代码
/** 2017-12-06 11:56:55.565738 HighlightedSearch[4419:1869486] w 2017-12-06 11:56:55.566287 HighlightedSearch[4419:1869486] 开始匹配,开始时间:2017-12-06 03:56:55 +0000 2017-12-06 11:56:55.626184 HighlightedSearch[4419:1869486] 匹配结束,结束时间:2017-12-06 03:56:55 +0000,耗时:0.0601 2017-12-06 11:56:55.937535 HighlightedSearch[4419:1869486] wa 2017-12-06 11:56:55.937842 HighlightedSearch[4419:1869486] 开始匹配,开始时间:2017-12-06 03:56:55 +0000 2017-12-06 11:56:55.983074 HighlightedSearch[4419:1869486] 匹配结束,结束时间:2017-12-06 03:56:55 +0000,耗时:0.0452 2017-12-06 11:56:56.344808 HighlightedSearch[4419:1869486] wan 2017-12-06 11:56:56.347350 HighlightedSearch[4419:1869486] 开始匹配,开始时间:2017-12-06 03:56:56 +0000 2017-12-06 11:56:56.414215 HighlightedSearch[4419:1869486] 匹配结束,结束时间:2017-12-06 03:56:56 +0000,耗时:0.0690 2017-12-06 11:56:56.711174 HighlightedSearch[4419:1869486] wang 2017-12-06 11:56:56.712013 HighlightedSearch[4419:1869486] 开始匹配,开始时间:2017-12-06 03:56:56 +0000 2017-12-06 11:56:56.774761 HighlightedSearch[4419:1869486] 匹配结束,结束时间:2017-12-06 03:56:56 +0000,耗时:0.0632 */
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(self.dataSource.count, queue, ^(size_t index) {
复制代码
/** 2017-12-06 11:58:12.716606 HighlightedSearch[4428:1869917] w 2017-12-06 11:58:12.717005 HighlightedSearch[4428:1869917] 开始匹配,开始时间:2017-12-06 03:58:12 +0000 2017-12-06 11:58:12.780168 HighlightedSearch[4428:1869917] 匹配结束,结束时间:2017-12-06 03:58:12 +0000,耗时:0.0633 2017-12-06 11:58:13.058590 HighlightedSearch[4428:1869917] wa 2017-12-06 11:58:13.058841 HighlightedSearch[4428:1869917] 开始匹配,开始时间:2017-12-06 03:58:13 +0000 2017-12-06 11:58:13.116964 HighlightedSearch[4428:1869917] 匹配结束,结束时间:2017-12-06 03:58:13 +0000,耗时:0.0581 2017-12-06 11:58:13.397052 HighlightedSearch[4428:1869917] wan 2017-12-06 11:58:13.397338 HighlightedSearch[4428:1869917] 开始匹配,开始时间:2017-12-06 03:58:13 +0000 2017-12-06 11:58:13.460298 HighlightedSearch[4428:1869917] 匹配结束,结束时间:2017-12-06 03:58:13 +0000,耗时:0.0630 2017-12-06 11:58:13.763888 HighlightedSearch[4428:1869917] wang 2017-12-06 11:58:13.764263 HighlightedSearch[4428:1869917] 开始匹配,开始时间:2017-12-06 03:58:13 +0000 2017-12-06 11:58:13.833888 HighlightedSearch[4428:1869917] 匹配结束,结束时间:2017-12-06 03:58:13 +0000,耗时:0.0697 */
dispatch_queue_t queue = dispatch_queue_create("wpf.updateSearchResults.test", DISPATCH_QUEUE_SERIAL);
[self.dataSource enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
复制代码
/** 2017-12-06 12:00:38.217187 HighlightedSearch[4439:1870645] w 2017-12-06 12:00:38.217575 HighlightedSearch[4439:1870645] 开始匹配,开始时间:2017-12-06 04:00:38 +0000 2017-12-06 12:00:38.253997 HighlightedSearch[4439:1870645] 匹配结束,结束时间:2017-12-06 04:00:38 +0000,耗时:0.0364 2017-12-06 12:00:38.616430 HighlightedSearch[4439:1870645] wa 2017-12-06 12:00:38.616807 HighlightedSearch[4439:1870645] 开始匹配,开始时间:2017-12-06 04:00:38 +0000 2017-12-06 12:00:38.654969 HighlightedSearch[4439:1870645] 匹配结束,结束时间:2017-12-06 04:00:38 +0000,耗时:0.0383 2017-12-06 12:00:38.948700 HighlightedSearch[4439:1870645] wan 2017-12-06 12:00:38.949453 HighlightedSearch[4439:1870645] 开始匹配,开始时间:2017-12-06 04:00:38 +0000 2017-12-06 12:00:38.986892 HighlightedSearch[4439:1870645] 匹配结束,结束时间:2017-12-06 04:00:38 +0000,耗时:0.0378 2017-12-06 12:00:39.280979 HighlightedSearch[4439:1870645] wang 2017-12-06 12:00:39.281563 HighlightedSearch[4439:1870645] 开始匹配,开始时间:2017-12-06 04:00:39 +0000 2017-12-06 12:00:39.317743 HighlightedSearch[4439:1870645] 匹配结束,结束时间:2017-12-06 04:00:39 +0000,耗时:0.0365 */
for (WPFPerson *person in self.dataSource) {
复制代码
最终选择的是forin循环,由于通常状况下
enumerateObjectsWithOptions
多线程是最快的,而且稍快于dispatch_apply
方法,可是由于这个方法须要操做数组,所以必须将操做数据的那行代码加锁或者在指定线程进行,进行这个操做后效率反而不如其余单线程循环,考虑到搜索结果原本还要再次根据规则排序,就选择了 forin 循环多线程
简单测了一下拥有该功能的产品:并发
// WPFPinYinDataManager 依次添加数据源(标识符为了防止重名现象)
+ (void)addInitializeString:(NSString *)string identifer:(NSString *)identifier
// 更新搜索结果
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
...
...
for (WPFPerson *person in [WPFPinYinDataManager getInitializedDataSource]) {
WPFSearchResultModel *resultModel = [WPFPinYinTools searchEffectiveResultWithSearchString:keyWord Person:person];
if (resultModel.highlightedRange.length) {
person.highlightLoaction = resultModel.highlightedRange.location;
person.textRange = resultModel.highlightedRange;
person.matchType = resultModel.matchType;
[resultDataSource addObject:person];
}
}
复制代码
最后再附一下demo地址app