仿趣頭條獲取系統通訊錄,並自定義通訊錄介面
阿新 • • 發佈:2018-12-09
我們有個專案 需求要做一個方趣頭條的獲取通訊錄的要求,在此期間,對搜尋欄和邊欄首字母檢索,有些陌生,踩了一些坑。
先來看效果
首先是獲取系統通訊錄,在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