我们有个项目 需求要做一个方趣头条的获取通讯录的要求,在此期间,对搜索栏和边栏首字母检索,有些陌生,踩了一些坑。
先来看效果
首先是获取系统通讯录,在iOS9之后,iOS对通讯录的库有了很大的改善。用起来很方便,但是点要注意在引用
#import <ContactsUI/ContactsUI.h>
#import <Contacts/Contacts.h>
这两个库的时候,需要手动添加.framework库如下:
如果不手动添加 会出一个 clang 文件的错
“XLContact.h”
//
// XLContact.m
// mahjonghn
//
// Created by 磊怀王 on 2018/12/6.
//
#import "XLContact.h"
#import <ContactsUI/ContactsUI.h>
#import <Contacts/Contacts.h>
#import "XLDIYContactViewController.h"
@implementation XLContact
+ (instancetype)shareXLContact{
static XLContact * _contact = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_contact = [[super allocWithZone:NULL] init];
});
return _contact;
}
//判断用户授权 通讯录
- (void)CheckAddressBookAuthorization:(void (^)(bool isAuthorized))block{
CNContactStore * contactStore = [[CNContactStore alloc]init];
if ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusNotDetermined) {
[contactStore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * __nullable error) {
if (error){
NSLog(@"Error: %@", error);
self.isAuthorized = @"0";
[UIApplication.sharedApplication.keyWindow makeToast:@"授权失败" duration:2.0f position:CSToastPositionCenter];
block(NO);
}else if (!granted){
self.isAuthorized = @"0";
[UIApplication.sharedApplication.keyWindow makeToast:@"授权失败" duration:2.0f position:CSToastPositionCenter];
block(NO);
}else{
self.isAuthorized = @"1";
[self abtainContact:contactStore];
block(YES);
}
}];
}else if ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusAuthorized){
self.isAuthorized = @"1";
[self abtainContact:contactStore];
block(YES);
}else if ([CNContactStore authorizationStatusForEntityType:CNAuthorizationStatusRestricted]){
NSLog(@"用户无法通过本机的设置权限打开授权,可能是什么家长模式");
[UIApplication.sharedApplication.keyWindow makeToast:@"类似于需要家长授权" duration:2.0f position:CSToastPositionCenter];
block(NO);
}else {
[UIApplication.sharedApplication.keyWindow makeToast:@"未授权" duration:2.0f position:CSToastPositionCenter];
NSLog(@"请到设置>隐私>通讯录打开本应用的权限设置");
block(NO);
}
}
//获取联系人
- (void)abtainContact:(CNContactStore *)store{
if (self.contactArr) {
return;
}
CNContactFetchRequest * request = [[CNContactFetchRequest alloc] initWithKeysToFetch:@[[CNContactFormatter descriptorForRequiredKeysForStyle:CNContactFormatterStyleFullName],CNContactPhoneNumbersKey,CNContactImageDataKey]];
NSError * error = nil;
self.contactArr = [[NSMutableArray alloc] init];
[store enumerateContactsWithFetchRequest:request error:&error usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {
NSMutableDictionary * oneContact = [[NSMutableDictionary alloc] init];
//联系人多个手机号的数组
NSMutableArray * contactNumArr = [[NSMutableArray alloc] init];
if (contact.imageData == nil) {
[oneContact setObject:[UIImage imageNamed:@"profile_placehoder_header.png"] forKey:@"contact_icon"];
}else{
[oneContact setObject:[UIImage imageWithData:contact.imageData] forKey:@"contact_icon"];
}
[oneContact setObject:[NSString stringWithFormat:@"%@%@",contact.familyName,contact.givenName] forKey:@"contact_name"];
for (CNLabeledValue * lab in contact.phoneNumbers) {
CNPhoneNumber * phone = (CNPhoneNumber *)lab.value;
NSString * phoneKey = [[[[lab.label componentsSeparatedByString:@"<"] lastObject] componentsSeparatedByString:@">"] firstObject];
[contactNumArr addObject:@{phoneKey:phone.stringValue}];
}
[oneContact setObject:contactNumArr forKey:@"contact_phones"];
[self.contactArr addObject:oneContact];
}];
}
- (void)showDIYContactCurrentController:(UIViewController *)currentController{
XLDIYContactViewController * contact = [[XLDIYContactViewController alloc] init];
contact.contactArr = self.contactArr;
[currentController.navigationController pushViewController:contact animated:YES];
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
return [XLContact shareXLContact];
}
- (id)copyWithZone:(nullable NSZone *)zone {
return [XLContact shareXLContact];
}
- (id)mutableCopyWithZone:(nullable NSZone *)zone {
return [XLContact shareXLContact];
}
@end
//
// XLContact.h
// mahjonghn
//
// Created by 磊怀王 on 2018/12/6.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface XLContact : NSObject
+ (instancetype)shareXLContact;
/**
检查用户对于通讯录的授权的设置
@param block 授权结果
*/
- (void)CheckAddressBookAuthorization:(void (^)(bool isAuthorized))block;
/** 存放联系人的数组 */
@property (nonatomic,strong) NSMutableArray * contactArr;
/**
* 通讯录 授权状态
*/
@property (nonatomic,copy) NSString * isAuthorized;
/**
展示自定义通讯录
@param currentController 当前的controller
*/
- (void)showDIYContactCurrentController:(UIViewController *)currentController;
@end
NS_ASSUME_NONNULL_END
然后我们自定义一个通讯录的model类
这里要注意,我们定义这个model类一方面用来存储联系人,一方面用来定义 contact_name 来让 代码对 联系人按照首字母进行排序
//
// XLSortContactLetter.h
// mahjonghn
//
// Created by 磊怀王 on 2018/12/6.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface XLSortContactLetter : NSObject
/** 排序后的首字母数组 */
@property (nonatomic,strong) NSMutableArray * contactEndLetterArr;
/** 排序后的联系人数组 */
@property (nonatomic,strong) NSMutableArray * sortEndContactArr;
/** 联系人排序数组 */
- (void)sortContact:(NSArray *)originalArr;
@property (nonatomic,copy) NSString * contact_name;
@property (nonatomic,strong) UIImage * contact_icon;
@property (nonatomic,strong) NSArray * contact_phones;
@end
NS_ASSUME_NONNULL_END
注意这句代码NSArray *sortedPersonArrayForSection = [collation sortedArrayFromArray:personArrayForSection collationStringSelector:@selector(contact_name)];,我当时查资料是,始终不明白这个@selector(contact_name) 中的contact_name 方法那儿来的,后来自己琢磨才明白,原来是要定义contact_name 属性,自动生成的get、set方法。
//
// XLSortContactLetter.m
// mahjonghn
//
// Created by 磊怀王 on 2018/12/6.
//
#import "XLSortContactLetter.h"
@interface XLSortContactLetter ()
@property (nonatomic,strong) NSMutableArray * operationArr;
@property (nonatomic,strong) NSMutableArray * sortContactArr;
@property (nonatomic,strong) NSMutableArray * contactLetterArr;
@end
@implementation XLSortContactLetter
- (void)sortContact:(NSArray *)originalArr{
//按照字母顺序 生成同等维度的二维数组
for (int i = 0; i < self.contactLetterArr.count; i++) {
NSMutableArray * arr = [[NSMutableArray alloc] init];
[self.sortContactArr addObject:arr];
}
//添加model
for (int i = 0; i < originalArr.count; i++) {
XLSortContactLetter * sortCon = [[XLSortContactLetter alloc] init];
sortCon.contact_name = [originalArr[i] objectForKey:@"contact_name"];
sortCon.contact_icon = [originalArr[i] objectForKey:@"contact_icon"];
sortCon.contact_phones = [originalArr[i] objectForKey:@"contact_phones"];
[self.operationArr addObject:sortCon];
}
if (originalArr.count == 0) {
return;
}else{
UILocalizedIndexedCollation * collation = [UILocalizedIndexedCollation currentCollation];
NSInteger sectionTitleCount = [[collation sectionTitles] count];
//按姓名分组
[self.operationArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSInteger sectionIndex = [collation sectionForObject:obj collationStringSelector:@selector(contact_name)];
[self.sortContactArr[sectionIndex] addObject:obj];
}];
//同一姓氏排序
for (int i = 0; i < sectionTitleCount; i++) {
NSMutableArray * personArrayForSection = self.sortContactArr[i];
NSArray *sortedPersonArrayForSection = [collation sortedArrayFromArray:personArrayForSection collationStringSelector:@selector(contact_name)];
self.sortContactArr[i] = sortedPersonArrayForSection;
}
//删除空姓氏数组
[self.sortContactArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSArray * tempArr = (NSArray *)obj;
if (tempArr.count != 0) {
[self.sortEndContactArr addObject:tempArr];
[self.contactEndLetterArr addObject:self.contactLetterArr[idx]];
}
}];
}
}
- (NSMutableArray *)operationArr{
if (!_operationArr) {
_operationArr = [[NSMutableArray alloc] init];
}
return _operationArr;
}
- (NSMutableArray *)sortEndContactArr{
if (!_sortEndContactArr) {
_sortEndContactArr = [[NSMutableArray alloc] init];
}
return _sortEndContactArr;
}
- (NSMutableArray *)sortContactArr{
if (!_sortContactArr) {
_sortContactArr = [[NSMutableArray array] init];
}
return _sortContactArr;
}
- (NSMutableArray *)contactEndLetterArr{
if (!_contactEndLetterArr) {
_contactEndLetterArr = [[NSMutableArray alloc] init];
}
return _contactEndLetterArr;
}
- (NSMutableArray *)contactLetterArr{
if (!_contactLetterArr) {
_contactLetterArr = [NSMutableArray arrayWithObjects:@"A",@"B",@"C",@"D",@"E",@"F",@"G",@"H",@"I",@"J",@"K",@"L",@"M",@"N",@"O",@"P",@"Q",@"R",@"S",@"T",@"U",@"V",@"W",@"X",@"Y",@"Z",@"#", nil];
}
return _contactLetterArr;
}
@end
XLDIYContactViewController.h
//
// XLDIYContactViewController.h
// mahjonghn
//
// Created by 磊怀王 on 2018/12/6.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface XLDIYContactViewController : UIViewController
//接收通讯录中返回来的通讯录的数组,注意这是没有model化的数组
@property (nonatomic,strong) NSArray * contactArr;
@end
NS_ASSUME_NONNULL_END
//
// XLDIYContactViewController.m
// mahjonghn
//
// Created by 磊怀王 on 2018/12/6.
//
#import "XLDIYContactViewController.h"
#import "XLSortContactLetter.h"
#import "XLShowContactTableViewCell.h"
#import "XLSortContactLetter.h"
@interface XLDIYContactViewController ()<UITableViewDelegate,UITableViewDataSource,UISearchResultsUpdating,UISearchBarDelegate>
{
BOOL _isSelectAll;
NSMutableArray * _contactNameArr; /** 对联系人的名字做一个数组 */
}
@property (nonatomic,strong) UITableView * tableView;
/**
搜索栏
*/
@property (nonatomic,strong) UISearchController * searchController;
/**
搜索结果的数组
*/
@property (nonatomic,strong) NSMutableArray * searchEndArr;
/**
展示出首字母的数组
*/
@property (nonatomic,strong) NSArray * letterArr;
/**
展示出来的联系人数组
*/
@property (nonatomic,strong) NSArray * showContactArr;
/**
选择出来的联系人的手机号的数组
*/
@property (nonatomic,strong) NSMutableArray * selectContentArr;
@end
@implementation XLDIYContactViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.title = @"邀请好友";
[self configurationBaseData];
if (self.showContactArr.count > 0) {
[self createTableView];
[self createBottomView];
[self createSearchController];
}else{
XLEmptyView * empty = [[XLEmptyView alloc] init];
[empty setupWithTarget:self.view];
empty.dataCount = self.showContactArr.count;
empty.desStr = @"您的通讯录空荡荡";
}
}
/**
配置基本通讯录数据
*/
- (void)configurationBaseData{
XLSortContactLetter * sort = [[XLSortContactLetter alloc] init];
[sort sortContact:self.contactArr];
self.showContactArr = [NSArray arrayWithArray:sort.sortEndContactArr];
self.letterArr = [NSArray arrayWithArray:sort.contactEndLetterArr];
_isSelectAll = NO;
_contactNameArr = [[NSMutableArray alloc] init];
for (NSArray * tempArr in self.showContactArr) {
for (XLSortContactLetter * letter in tempArr) {
[_contactNameArr addObject:letter.contact_name];
}
}
}
/**
初始化 tableview
*/
- (void)createTableView{
CGFloat bottomHeight = kTabbarHeight;
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, kStatusHeight + 44, kScreenWidth, kScreenHeight - kStatusHeight - 44 - bottomHeight) style:UITableViewStylePlain];
self.tableView.backgroundColor = [UIColor whiteColor];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.view addSubview:self.tableView];
self.tableView.sectionIndexColor = [UIColor colorWithHex:0x666666];
kTableIos11
}
/**
初始化 搜索栏
*/
- (void)createSearchController{
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.hidesNavigationBarDuringPresentation = NO;
self.searchController.searchBar.delegate = self;
self.searchController.searchBar.frame = CGRectMake(self.searchController.searchBar.frame.origin.x, self.searchController.searchBar.frame.origin.y, self.searchController.searchBar.frame.size.width, 56);
self.searchController.searchBar.placeholder = @"搜索联系人";
[self.searchController.searchBar setValue:@"完成"forKey:@"_cancelButtonText"];
self.tableView.tableHeaderView = self.searchController.searchBar;
}
/**
创建底部按钮
*/
- (void)createBottomView{
CGFloat bottomHeight = kTabbarHeight;
UIView * bottomV = [[UIView alloc] initWithFrame:CGRectMake(0, kScreenHeight - bottomHeight, kScreenWidth, 49)];
bottomV.backgroundColor = [UIColor whiteColor];
[self.view addSubview:bottomV];
UIButton * selectAll = [UIButton buttonWithType:UIButtonTypeCustom];
selectAll.frame = CGRectMake(0, 0, 49, 49);
selectAll.selected = NO;
[selectAll setImage:[UIImage imageNamed:@"profile_selectNoContact.png"] forState:UIControlStateNormal];
[bottomV addSubview:selectAll];
[selectAll addTarget:self action:@selector(selectAllContact:) forControlEvents:UIControlEventTouchUpInside];
UILabel * titleLab = [[UILabel alloc] init];
titleLab.text = @"批量选择";
titleLab.textColor = [UIColor colorWithHex:0x3E3E3E];
[bottomV addSubview:titleLab];
titleLab.font = [UIFont systemFontOfSize:16];
[titleLab mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(selectAll.mas_right).offset(0);
make.right.mas_equalTo(bottomV).offset(100);
make.centerY.equalTo(bottomV);
make.width.offset(100);
make.height.offset(22);
}];
UIButton * invitationBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[bottomV addSubview:invitationBtn];
[invitationBtn setTitle:@"短信邀请" forState:UIControlStateNormal];
[invitationBtn setTitleColor:[UIColor colorWithHex:0x3E3E3E] forState:UIControlStateNormal];
[invitationBtn setBackgroundColor:kMainColor];
invitationBtn.titleLabel.font = [UIFont systemFontOfSize:16];
[invitationBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.right.bottom.offset(0);
make.left.mas_equalTo(bottomV).offset(ScaleSize(247));
}];
[invitationBtn addTarget:self action:@selector(sendMessageAction:) forControlEvents:UIControlEventTouchUpInside];
}
#pragma mark -------- tableView delegate -----
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return ScaleSize(60);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
if (self.searchController.active == YES) {
return 1;
}else{
return self.letterArr.count;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if (self.searchController.active == YES) {
return [self.searchEndArr count];
}else{
return [self.showContactArr[section] count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString * idfire = @"XLShowContactTableViewCell";
XLShowContactTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:idfire];
if (!cell) {
[tableView registerNib:[UINib nibWithNibName:@"XLShowContactTableViewCell" bundle:nil] forCellReuseIdentifier:idfire];
cell = [tableView dequeueReusableCellWithIdentifier:idfire];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
if (self.searchController.active == YES) {
XLSortContactLetter * model = self.searchEndArr[indexPath.row];
[cell addContantForCell:model contactSelect:_isSelectAll];
}else{
XLSortContactLetter * model = self.showContactArr[indexPath.section][indexPath.row];
[cell addContantForCell:model contactSelect:_isSelectAll];
}
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
if (self.searchController.active == YES) {
return 0.00001;
}else{
return ScaleSize(30);
}
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
if (self.searchController.active == YES) {
UIView * vi = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 0.00001)];
return vi;
}else{
UIView * vi = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, ScaleSize(30))];
vi.backgroundColor = [UIColor colorWithHex:0xEDEDED];
UILabel * lab = [[UILabel alloc] init];
[vi addSubview:lab];
lab.text = self.letterArr[section];
lab.textColor = [UIColor colorWithHex:0x3E3E3E];
lab.font = [UIFont systemFontOfSize:16 weight:UIFontWeightBold];
[lab mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(vi).offset(ScaleSize(16));
make.right.top.bottom.mas_equalTo(vi).offset(0);
}];
return vi;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
return 0.00001;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{
UIView * vi = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 0.0001)];
vi.backgroundColor = [UIColor clearColor];
return vi;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
XLShowContactTableViewCell * cell = [tableView cellForRowAtIndexPath:indexPath];
cell.isSelectContact = !cell.isSelectContact;
XLSortContactLetter * model = self.showContactArr[indexPath.section][indexPath.row];
for (NSDictionary * tempDic in model.contact_phones) {
for (NSString * tempStr in tempDic) {
if (cell.isSelectContact == YES) {
[self.selectContentArr addObject:tempDic[tempStr]];
}else{
[self.selectContentArr removeObject:tempDic[tempStr]];
}
}
}
}
//添加右侧索引栏
- (NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView{
return self.letterArr;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
return self.letterArr[section];
}
#pragma mark ---- search searchupdatedelegate ----
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController{
NSString * searchStr = searchController.searchBar.text;
NSPredicate *preicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@", searchStr];
if (self.searchEndArr!= nil) {
[self.searchEndArr removeAllObjects];
}
/**
* 对 通讯录的人进行检索,这里注意 SELF CONTAINS %@ 是对所要搜索的字符串,这里也就是
人名进行搜索, 所以,被搜索的对象应该是一个 只装载人名的数组,也就是指装载字符串的数组,
因此要对装的联系人model的数组,进行便利得到只有联系人人名的数组,这里指的是_contactNameArr
对联系人人名进行检索之后,会得到 tempSearhArr 这个数组也是只包含联系人人名
然后,在根据联系人人名 去装着model的数组(self.showContactArr) 中查找名字对应的model,并且存放在
self.searchEndArr 中。
*/
NSArray * tempSearhArr = [NSMutableArray arrayWithArray:[_contactNameArr filteredArrayUsingPredicate:preicate]];
for (NSString * tempStr in tempSearhArr) {
for (NSArray * tempArr in self.showContactArr) {
for (XLSortContactLetter * letter in tempArr) {
if ([tempStr isEqualToString:letter.contact_name] == YES) {
[self.searchEndArr addObject:letter];
break;
}
}
}
}
//刷新表格
[self.tableView reloadData];
}
#pragma mark ---- 底部按钮的点击事件 ----
- (void)selectAllContact:(UIButton *)btn{
if (btn.selected == NO) {
[btn setImage:[UIImage imageNamed:@"profile_selectContact.png"] forState:UIControlStateNormal];
}else{
[btn setImage:[UIImage imageNamed:@"profile_selectNoContact.png"] forState:UIControlStateNormal];
}
btn.selected = !btn.selected;
_isSelectAll = btn.selected;
[self.tableView reloadData];
if (self.selectContentArr) {
[self.selectContentArr removeAllObjects];
if (_isSelectAll == YES) {
for (XLSortContactLetter * letter in self.showContactArr) {
for (NSDictionary * tempDic in letter.contact_phones) {
for (NSString * tempStr in tempDic) {
[self.selectContentArr addObject:tempDic[tempStr]];
}
}
}
}
}
}
- (void)sendMessageAction:(UIButton *)btn{
if (self.selectContentArr.count == 0) {
return;
}
}
- (NSMutableArray *)selectContentArr{
if (!_selectContentArr) {
_selectContentArr = [[NSMutableArray alloc] init];
}
return _selectContentArr;
}
- (NSMutableArray *)searchEndArr{
if (!_searchEndArr) {
_searchEndArr = [[NSMutableArray alloc] init];
}
return _searchEndArr;
}
@end
就这些代码了,这里要注意,需要自己去写 cell 的代码,我没上传,非常简单,我就不传了
我是磊怀 如有疑问,请联系我 QQ: 2849765859