欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-02-27 21:11:03
...

 

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

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

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

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

屏幕快照 2018-08-13 上午11.59.34.png

cell xib

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

屏幕快照 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;
}