iOS通讯录开发(排序、搜索、头像处理)

开发过一打电话的APP,把其中通讯录处理拿出来分享一下。
先上效果图

2018-08-13 14_40_38.gif

.h

#import 

@protocol PhoneBookViewControllerDelegate 

/**
 * 返回联系人信息
 * @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 delegate;

/** 通讯录数据 */
@property (nonatomic, strong) NSMutableDictionary       *dataSource;
/** 首字母数组 */
@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 
#import 
#import "PhoneBookTableViewCell.h"

@interface PhoneBookViewController ()
@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] characterAtIndex:0] > [_sectionTitleArray[j] characterAtIndex:0]) {
                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;
}

你可能感兴趣的:(iOS通讯录开发(排序、搜索、头像处理))