实现通讯录分组排序的一个方法

字数 829阅读 2587

在APP开发中,我们经常会去做一个通讯录。所有做iOS开发的都知道这玩意儿应该用UITableView来做。但是,如何将数据分组排序总是一个比较麻烦的问题,这里提供我个人的解决方法供大家参考。


排序的目标

因为要是用UITableView,所以我们的数据源应该是这样一个数组:数组dataArray拥有27个子数组subArray,前26个subArray存储以A到Z开头的联系人,第27个数组存储不以字母开头的联系人,也就是我们常见的#。另外我们需要一个titileArray数组用来作为数组右侧的索引和每个section的标题。
再次明确一下我们的目标:一个dataArray,包含27个subArray,另外需要一个titleArray。

获取首字母

如果我们的联系人都是英文名,这一步就很简单,但是有中文名怎么办呢? 没关系,OC其实给我们提供了一个方法用来将汉字转化成拼音,这样我们就能很方便的取到首字母了。
我们首先写下获取首字母的方法

- (NSString *)returnFirstWordWithString:(NSString *)str
{
NSMutableString * mutStr = [NSMutableString stringWithString:str];

 //将mutStr中的汉字转化为带音标的拼音(如果是汉字就转换,如果不是则保持原样)
CFStringTransform((__bridge CFMutableStringRef)mutStr, NULL, kCFStringTransformMandarinLatin, NO);
//将带有音标的拼音转换成不带音标的拼音(这一步是从上一步的基础上来的,所以这两句话一句也不能少)
CFStringTransform((__bridge CFMutableStringRef)mutStr, NULL, kCFStringTransformStripCombiningMarks, NO);
if (mutStr.length >0) {
     //全部转换为大写    取出首字母并返回
     NSString * res = [[mutStr uppercaseString] substringToIndex:1];
    return res;
}
else
    return @"";

}
按首字母分组排序

前面说过,我们的27分别代表从A到Z再加上#,那么我们这一步就是要将上一步获得的首字母所对应的联系人放到相应的数组中去(比如把 奔跑的炸鸡放到第二个数组,因为它代表B开头的联系人)。
那我们需要写27个if语句吗?
当然不是,我们有更简单的方法,但是我们需要先回忆两个小知识点

  1. 记得A到Z对应的ASCⅡ码吗?是65到90。在Unicode编码中,A到Z对应的也是65到90。
  1. 记得在C语言中,一个char类型其实就是一个字节的int类型吗?如果将char作为int去取值,那么A就是65,Z就是90。

通过以上两点我们就可以将拿到首字母的联系人直接放到对应的数组,无需任何比较

接下来是代码

- (void)dealDataWithArray:(NSArray *)array
{

NSMutableArray * tmpArray = [[NSMutableArray alloc]init];
for (NSInteger i =0; i <27; i++) {
    //给临时数组创建27个数组作为元素,用来存放A-Z和#开头的联系人
    NSMutableArray * array = [[NSMutableArray alloc]init];
    [tmpArray addObject:array];
}

for (AddressModel * model in array) {
     //AddressMode是联系人的数据模型
    //转化为首拼音并取首字母
    NSString * nickName = [FunctionModule returnFirstWordWithString:model.nickname];
    int firstWord = [nickName characterAtIndex:0];
    
    //把字典放到对应的数组中去
    
    if (firstWord >= 65 && firstWord <= 90) {
   //如果首字母是A-Z,直接放到对应数组
            NSMutableArray * array = tmpArray[firstWord - 65];
            [array addObject:model];
        
    }
    else
    { 
        //如果不是,就放到最后一个代表#的数组
        NSMutableArray * array =[tmpArray lastObject];
        [array addObject:model];
    }
}

//此时数据已按首字母排序并分组
//遍历数组,删掉空数组
for (NSMutableArray * mutArr in tmpArray) {
    //如果数组不为空就添加到数据源当中
    if (mutArr.count != 0) {
        [self.dataArray addObject:mutArr];
        AddressModel * model = mutArr[0];
        NSString * nickName = [FunctionModule returnFirstWordWithString:model.nickname];
        int firstWord = [nickName characterAtIndex:0];
        //取出其中的首字母放入到标题数组,暂时不考虑非A-Z的情况
        if (firstWord >= 65 && firstWord <= 90) {
            [self.titleArray addObject:nickName];
        }
    }
}
//便利结束后,两个数组数目不相等说明有除大写字母外的其他首字母
if (!(self.titleArray.count == self.dataArray.count)) {
    [self.titleArray addObject:@"#"];
}

//刷新tableView
[self.tableView reloadData];
}
效果展示
效果展示
一些不算坑的坑

1.右侧的索引列表,网上给出的答案是实现 - (NSArray *)titleArrayAtIndexes:(NSIndexSet *)indexes方法,但是实际上这个方法已经无效了,现在应该去实现
- (NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return self.titleArray;
}

2.还是索引,默认的索引是文字是蓝色的,还会打一个白色的半透明图层,这两个东西其实是可以直接设置的。代码如下:

    //设置索引文字颜色
    self.tableView.sectionIndexColor = ColorFromRGB(0x646464);
    //设置索引背景颜色
    self.tableView.sectionIndexBackgroundColor = [UIColor clearColor];

3.section的那种悬浮效果,只有当tableView的风格是Plain的时候才会有。

4.tableView底部多余空白Cell可以通过设置TableViewFooter解决

self.tableView.tableFooterView = [UIView new];

结语

最近事情有点多,拖了很久才写。
这里只是提供个人使用的方法,如果有错误之处欢迎指出。
如果我的文章对您有帮助,请点赞或评论。
我们下篇文章见!

推荐阅读更多精彩内容