iOS通讯录开发(排序、搜索、头像处理)
程序员文章站
2024-02-27 21:11:03
...
开发过一打电话的APP,把其中通讯录处理拿出来分享一下。
先上效果图
2018-08-13 14_40_38.gif
.h
#import <UIKit/UIKit.h>
@protocol PhoneBookViewControllerDelegate <NSObject>
/**
* 返回联系人信息
* @param phoneNote 电话备注
* @param phones 电话号码数组
*/
- (void)action_Note:(NSString *)phoneNote phones:(NSArray *)phones;
@end
@interface PhoneBookViewController : UIViewController
@property (nonatomic, strong) NSArray *arrayWithTableV;
@property (nonatomic, weak) id<PhoneBookViewControllerDelegate> delegate;
/** 通讯录数据 <NSString,NSMutableArray>*/
@property (nonatomic, strong) NSMutableDictionary *dataSource;
/** 首字母数组 <NSString>*/
@property (nonatomic, strong) NSMutableArray *sectionTitleArray;
@end
xib
屏幕快照 2018-08-13 上午11.59.34.png
cell xib
屏幕快照 2018-08-13 下午2.55.53.png
.m
#import "PhoneBookViewController.h"
#import <AddressBookUI/ABNewPersonViewController.h>
#import <Contacts/Contacts.h>
#import "PhoneBookTableViewCell.h"
@interface PhoneBookViewController ()<UITableViewDelegate,UITableViewDataSource,ABNewPersonViewControllerDelegate,UITextFieldDelegate>
@property (weak, nonatomic) IBOutlet UITableView *tableV;
@property (weak, nonatomic) IBOutlet UIView *searchView;//搜索view
@property (weak, nonatomic) IBOutlet UITextField *searchTextField;
@property (weak, nonatomic) IBOutlet UIButton *closeButton;
@property (weak, nonatomic) IBOutlet UIButton *cancelButton;
@property (weak, nonatomic) IBOutlet UILabel *shadowLabel;
@property (nonatomic, strong) NSMutableArray *arrayWithSearch;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *tableVTop;
/** 记录TableView开始搜索时的contentOffset */
@property (nonatomic, assign) CGPoint oldPoint;
@end
@implementation PhoneBookViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIBarButtonItem *item = [UIBarButtonItem backItemWithImage:IMGNAME(@"返回") addTarget:self action:@selector(back) title:@"返回"];
self.title = @"通讯录";
self.navigationItem.leftBarButtonItem = item;
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithImage:IMGNAME(@"新建联系人") style:UIBarButtonItemStylePlain target:self action:@selector(newPhone)];
[self getUi];
/** 在进入APP的时候获取通讯录(getContactList)并保存:Array -> 归档 -> UserDefaults
NSArray *arrayWithAddressBook = @[self.sectionTitleArray,self.dataSource];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arrayWithAddressBook];
[[NSUserDefaults standardUserDefaults]setObject:data forKey:@"addressBook"];
*/
//从UserDefaults里面获取缓存的通讯录
//UserDefaults -> 解档(反归档) -> Array
NSData *data = [[NSUserDefaults standardUserDefaults]objectForKey:@"addressBook"];
NSArray *arrayWithAddressBook = [NSKeyedUnarchiver unarchiveObjectWithData:data];
if (arrayWithAddressBook.count != 0) {
self.sectionTitleArray = [arrayWithAddressBook[0] mutableCopy];
self.dataSource = arrayWithAddressBook[1];
}
//如果UserDefaults里面没有就获取并保存通讯录信息
if (self.arrayWithTableV.count == 0) {
self.arrayWithTableV = [self getContactList];
[self addressBookOrdering:self.arrayWithTableV];
}
//view适配代码 -> https://www.jianshu.com/p/a8eb3bbdc00f
[XLBTools viewLayoutWithXib:self.view];
[XLBTools viewLayoutWithXib:self.searchView];
// Do any additional setup after loading the view from its nib.
}
- (void)back{
NavBack(YES);
}
- (void)getUi{
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 0, 0.0001)];
self.tableV.tableHeaderView = view;
[self.tableV registerNib:[UINib nibWithNibName:@"PhoneBookTableViewCell" bundle:nil] forCellReuseIdentifier:@"PhoneBookTableViewCell"];
}
#pragma mark - 新建联系人
- (void)newPhone{
//调用系统的新建联系人
ABNewPersonViewController *picker = [[ABNewPersonViewController alloc]init];
picker.newPersonViewDelegate = self;
UINavigationController *navigation = [[UINavigationController alloc]initWithRootViewController:picker];
[self presentViewController:navigation animated:YES completion:^{
//刷新UI
}];
}
#pragma mark - ABNewPersonViewControllerDelegate
- (void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(nullable ABRecordRef)person{
[newPersonView dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - 通讯录排序
- (void)addressBookOrdering:(NSArray *)array{
[self.sectionTitleArray removeAllObjects];
[self.dataSource removeAllObjects];
//赋初值 A~Z #
for (int i = 65; i < 91; i ++) {
char c = (char)i;
NSMutableArray *array = [NSMutableArray array];
NSString *key = [NSString stringWithFormat:@"%c",c];
[self.dataSource setObject:array forKey:key];
}
[self.dataSource setObject:[NSMutableArray array] forKey:@"#"];
//遍历联系人信息
for (CNContact *cnContact in array) {
//备注名
NSString *name = [NSString stringWithFormat:@"%@%@",cnContact.familyName,cnContact.givenName];
//判断是否有备注名
if (name.length == 0) {
CNLabeledValue *lableValue = cnContact.phoneNumbers[0];
name = lableValue.label;
}
if (name.length == 0) {
NSArray *arrayWithPhone = cnContact.phoneNumbers;
CNLabeledValue *labelValue = arrayWithPhone[0];
CNPhoneNumber *phoneNumber = labelValue.value;
name = phoneNumber.stringValue;
}
//获取备注名首字母
NSString *key = [self firstCharactorWithString:name];
NSMutableArray *ar = self.dataSource[key];
//保存备注名
NSMutableDictionary *dic = [@{@"name":name} mutableCopy];
NSMutableArray *phones = [NSMutableArray array];
NSArray *arrayWithPhone = cnContact.phoneNumbers;
for (CNLabeledValue *labelValue in arrayWithPhone) {
CNPhoneNumber *phoneNumber = labelValue.value;
[phones addObject:phoneNumber.stringValue];
}
//保存电话
[dic setObject:phones forKey:@"phones"];
NSData *data = cnContact.thumbnailImageData;
UIImage *image = [UIImage imageWithData:data];
//保存头像
if (image == nil) {
[dic setObject:@"" forKey:@"header"];
} else{
[dic setObject:image forKey:@"header"];
}
[ar addObject:dic];
//保存联系人信息
[self.dataSource setObject:ar forKey:key];
}
//删除多余的分类
for (NSString *key in self.dataSource.allKeys) {
NSArray *array = self.dataSource[key];
if (array.count == 0) {
[self.dataSource removeObjectForKey:key];
}
}
self.closeButton.hidden = YES;
[self.tableV reloadData];
}
#pragma mark - 通讯录搜索
- (void)searchPhone{
[self.arrayWithSearch removeAllObjects];
//搜索关键字(可以是数字、文字)
NSString *searchText = self.searchTextField.text;
if (searchText.length == 0) {
[self addressBookOrdering:self.arrayWithTableV];
self.closeButton.hidden = NO;
[self.tableV layoutIfNeeded];
[self.tableV setContentOffset:self.oldPoint animated:NO];
return;
}
//遍历获取搜索数据
for (CNContact *cnContact in self.arrayWithTableV) {
NSString *name = [NSString stringWithFormat:@"%@%@",cnContact.familyName,cnContact.givenName];
if (!([name rangeOfString:searchText].location == NSNotFound) ) {
[self.arrayWithSearch addObject:cnContact];
continue;
}
NSArray *arrayWithPhone = cnContact.phoneNumbers;
for (int i = 0; i < arrayWithPhone.count; i ++) {
CNLabeledValue *labelValue = arrayWithPhone[i];
CNPhoneNumber *phoneNumber = labelValue.value;
name = labelValue.label;
if (!([name rangeOfString:searchText].location == NSNotFound) ) {
[self.arrayWithSearch addObject:cnContact];
break;
}
name = [self dealWithPhone:phoneNumber.stringValue];
if (!([name rangeOfString:searchText].location == NSNotFound) ) {
[self.arrayWithSearch addObject:cnContact];
break;
}
}
}
//对搜索数据进行排序展示
[self addressBookOrdering:self.arrayWithSearch];
//因为使用同一个tableView所以滑动到顶部
[self.tableV layoutIfNeeded];
[self.tableV setContentOffset:CGPointMake(0, 0) animated:NO];
}
/** 处理手机号 */
- (NSString *)dealWithPhone:(NSString *)phone{
NSString *phoneNum = [NSString stringWithFormat:@"%@",phone];
phoneNum = [phoneNum stringByReplacingOccurrencesOfString:@" " withString:@""];
phoneNum = [phoneNum stringByReplacingOccurrencesOfString:@"-" withString:@""];
phoneNum = [phoneNum stringByReplacingOccurrencesOfString:@"+86" withString:@""];
phoneNum = [phoneNum stringByReplacingOccurrencesOfString:@"," withString:@""];
return phoneNum;
}
#pragma mark - 获取通讯录列表
-(NSMutableArray*)getContactList{
NSMutableArray *array = [NSMutableArray array];
//获取授权状态
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
//判断授权状态,如果不是已经授权,则直接返回
if (status != CNAuthorizationStatusAuthorized) {
return nil;
}
//创建通信录对象
CNContactStore *contactStore = [[CNContactStore alloc] init];
// 4.1.拿到所有打算获取的属性对应的key
NSArray *keys = @[CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey,CNContactImageDataKey,CNContactThumbnailImageDataKey,CNContactImageDataAvailableKey];
// 4.2.创建CNContactFetchRequest对象
CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:keys];
// 5.遍历所有的联系人
[contactStore enumerateContactsWithFetchRequest:request error:nil usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {
[array addObject:contact];
}
];
return array;
}
//获取某个字符串或者汉字的首字母.
- (NSString *)firstCharactorWithString:(NSString *)string
{
NSMutableString *str = [NSMutableString stringWithString:string];
CFStringTransform((CFMutableStringRef) str, NULL, kCFStringTransformMandarinLatin, NO);
CFStringTransform((CFMutableStringRef)str, NULL, kCFStringTransformStripDiacritics, NO);
NSString *pinYin = [str capitalizedString];
if (pinYin.length == 0) {
return @"#";
}
unichar c = [pinYin characterAtIndex:0];
if (c <'A'|| c >'Z'){
return @"#";
}
NSCharacterSet *notDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
if ([[pinYin substringToIndex:1] rangeOfCharacterFromSet:notDigits].location == NSNotFound){
// 是数字
return @"#";
}
if ([[pinYin substringToIndex:1] isEqual:@"_"]) {
return @"#";
}
return [pinYin substringToIndex:1];
}
#pragma mark - TableViewDelegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return self.sectionTitleArray.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
NSArray *array = [self.dataSource valueForKey:self.sectionTitleArray[section]];
return array.count;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return ADAPTATION_HEIGHT(67);
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
return ADAPTATION_HEIGHT(25);
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, MAINSCREEN_WIDTH, ADAPTATION_HEIGHT(24))];
UILabel *label = [[UILabel alloc]init];
label.text = [NSString stringWithFormat:@"%@",self.sectionTitleArray[section]];
[view addSubview:label];
label.textColor = GetColor(26, 26, 26, 1);
label.font = FONT(18);
label.frame = CGRectMake(ADAPTATION_WIDTH(11), 0, 100, ADAPTATION_HEIGHT(25));
return view;
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
return 0.0001;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
PhoneBookTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"PhoneBookTableViewCell" forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
NSArray *array = [self.dataSource valueForKey:self.sectionTitleArray[indexPath.section]];
NSDictionary *dic = array[indexPath.row];
NSString *name = dic[@"name"];
NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet];
name = [name stringByTrimmingCharactersInSet:set];
cell.name.text = name;
id image = dic[@"header"];
if ([image isKindOfClass:[UIImage class]]) {
cell.headerImage.image = dic[@"header"];
} else{
cell.headerImage.image = nil;
}
NSString *headerName = [name substringWithRange:NSMakeRange(name.length - 1, 1)];
cell.phoneNameImage.text = headerName;
NSInteger num = arc4random() % 5;
switch (num) {
case 1:
cell.phoneNameImage.backgroundColor = GetColor(134,185,255, 1);
break;
case 2:
cell.phoneNameImage.backgroundColor = GetColor(134,234,255, 1);
break;
case 3:
cell.phoneNameImage.backgroundColor = GetColor(129,212,255, 1);
break;
case 4:
cell.phoneNameImage.backgroundColor = GetColor(245,175,255, 1);
break;
default:
break;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSArray *array = [self.dataSource valueForKey:self.sectionTitleArray[indexPath.section]];
NSDictionary *dic = array[indexPath.row];
NSString *name = dic[@"name"];
NSArray *phones = dic[@"phones"];
NSLog(@"备注:%@,电话:%@",name,phones);
if (phones.count > 1) {
[self choosePhone:phones name:name];
} else{
if (_delegate && [self.delegate respondsToSelector:@selector(action_Note:phones:)]) {
[self.delegate action_Note:name phones:phones];
[self back];
}
}
}
//选择电话号码
- (void)choosePhone:(NSArray *)phones name:(NSString *)name{
[self.view endEditing:YES];
UIAlertController *alter = [UIAlertController alertControllerWithTitle:@"" message:@"请选择号码" preferredStyle:UIAlertControllerStyleActionSheet];
for (int i = 0 ; i < phones.count; i++) {
NSString *phone = phones[i];
UIAlertAction *action = [UIAlertAction actionWithTitle:phone style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[self chooseType:name phone:@[phone]];
}];
[alter addAction:action];
}
UIAlertAction *actionCancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
[alter addAction:actionCancel];
[self presentViewController:alter animated:YES completion:nil];
}
- (void)chooseType:(NSString *)name phone:(NSArray *)phones{
if (_delegate && [self.delegate respondsToSelector:@selector(action_Note:phones:)]) {
[self.delegate action_Note:name phones:phones];
[self back];
}
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return self.sectionTitleArray;
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
[self hiddenNavView];
return YES;
}
- (IBAction)textFieldValueChange:(UITextField *)sender {
[self searchPhone];
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField endEditing:YES];
[self searchPhone];
return YES;
}
//开始搜索
- (void)hiddenNavView{
if (self.navigationController.navigationBar.hidden == YES) {
return;
}
self.oldPoint = self.tableV.contentOffset;
for (UIView *view in self.searchView.subviews) {
CGRect rect = view.frame;
if (iPhoneX) {
rect.origin.y = rect.origin.y + 33;
} else{
rect.origin.y = rect.origin.y + 14;
}
view.frame = rect;
}
[UIView animateWithDuration:0.3 animations:^{
NavHidden(YES);
CGRect searchRect = self.searchView.frame;
if (iPhoneX) {
searchRect.size.height = 88;
} else{
searchRect.size.height = 64;
}
self.searchView.frame = searchRect;
self.closeButton.hidden = NO;
CGRect rect = self.searchTextField.frame;
rect.size.width = ADAPTATION_WIDTH(254);
self.searchTextField.frame = rect;
CGRect cancelRect = self.cancelButton.frame;
cancelRect.origin.x = ADAPTATION_WIDTH(330);
self.cancelButton.frame = cancelRect;
CGRect shadowRect = self.shadowLabel.frame;
shadowRect.size.width = shadowRect.size.width - ADAPTATION_WIDTH(40);
self.shadowLabel.frame = shadowRect;
self.tableVTop.constant = self.searchView.frame.size.height;
}];
}
//取消搜索
- (IBAction)showNavView{
self.searchTextField.text = @"";
[self addressBookOrdering:self.arrayWithTableV];
//tableview滑动到搜索之前所在位置
[self.tableV layoutIfNeeded];
[self.tableV setContentOffset:self.oldPoint animated:NO];
for (UIView *view in self.searchView.subviews) {
CGRect rect = view.frame;
if (iPhoneX) {
rect.origin.y = rect.origin.y - 33;
} else{
rect.origin.y = rect.origin.y - 14;
}
view.frame = rect;
}
[self.searchTextField endEditing:YES];
[UIView animateWithDuration:0.3 animations:^{
NavHidden(NO);
CGRect searchRect = self.searchView.frame;
searchRect.size.height = ADAPTATION_HEIGHT(51);
self.searchView.frame = searchRect;
self.closeButton.hidden = YES;
CGRect rect = self.searchTextField.frame;
rect.size.width = ADAPTATION_WIDTH(318);
self.searchTextField.frame = rect;
CGRect cancelRect = self.cancelButton.frame;
cancelRect.origin.x = MAINSCREEN_WIDTH + 1;
self.cancelButton.frame = cancelRect;
CGRect shadowRect = self.shadowLabel.frame;
shadowRect.size.width = shadowRect.size.width + ADAPTATION_WIDTH(40);
self.shadowLabel.frame = shadowRect;
self.tableVTop.constant = self.searchView.frame.size.height;
}];
}
#pragma mark - 视图生命周期
- (void)viewWillAppear:(BOOL)animated{
}
- (void)viewDidAppear:(BOOL)animated{
// 判断是否是从新建页面返回
if (![self isMovingToParentViewController]) {
self.arrayWithTableV = [self getContactList];
[self addressBookOrdering:self.arrayWithTableV];
}
}
#pragma mark - Getter/Setter
- (void)setArrayWithTableV:(NSArray *)arrayWithTableV{
_arrayWithTableV = arrayWithTableV;
}
- (void)setDataSource:(NSMutableDictionary *)dataSource{
_dataSource = dataSource;
}
- (NSMutableArray *)sectionTitleArray {
_sectionTitleArray = [[self.dataSource allKeys] mutableCopy];
for (NSInteger i = 0; i < _sectionTitleArray.count; i++) {
for (NSInteger j = _sectionTitleArray.count - 1; j > i; j--) {
if (_sectionTitleArray[i] > _sectionTitleArray[j]) {
NSString *temp = _sectionTitleArray[i];
[_sectionTitleArray replaceObjectAtIndex:i withObject:_sectionTitleArray[j]];
[_sectionTitleArray replaceObjectAtIndex:j withObject:temp];
}
}
}
[_sectionTitleArray removeObject:@"#"];
[_sectionTitleArray addObject:@"#"];
return _sectionTitleArray;
}
- (NSMutableArray *)arrayWithSearch{
if (!_arrayWithSearch) {
_arrayWithSearch = [NSMutableArray array];
}
return _arrayWithSearch;
}