zz做開發已經有一段時間了, 最近換了新公司, 決定寫一些東西記錄一下開發中遇到的坑. 新的公司做的是企業通訊方面的, 首先遇到的坑就是獲取手機通訊錄的信息.
由于版本適配的問題, 這次采用的是AddressBook這個框架, 這個框架在iOS9以后已經被Contacts所代替. 以后用到這個在做記錄吧,
廢話不多說了, 這次要做的就是把通訊錄的信息取出來然后自定義界面展示.第一步要做的就是引入框架頭文件
import <AddressBook/AddressBook.h>
由于訪問的是通訊錄要獲得用戶授權所有我們要寫一個方法獲取用戶的授權 直接貼上代碼吧
新建一個model類, 做聯系人的model
屬性如下, 因為我只需要這幾個所以就只寫著幾個了
#import "BaseModel.h"
#import <AddressBook/AddressBook.h> @interface ContactBookModel : BaseModel<NSCoding> @property (nonatomic, strong) NSString *name, *telephone, *lastName, *characterName; @property (nonatomic, strong) NSData *imageData; @property (nonatomic, assign) BOOL isSelect;
@end
下面是獲取通訊錄, 我們可以寫一個類專門處理這個, 因為在通訊錄的開發中 , 我們可以在不同的控制器里會用到這個
-(void)getrightFromUser { //這個變量用于記錄授權是否成功,即用戶是否允許我們訪問通訊錄 int __block tip=0; //聲明一個通訊簿的引用 ABAddressBookRef addBook =nil; //創建通訊簿的引用 addBook=ABAddressBookCreateWithOptions(NULL, NULL); //創建一個出事信號量為0的信號 dispatch_semaphore_t sema=dispatch_semaphore_create(0); //申請訪問權限 ABAddressBookRequestAccessWithCompletion(addBook, ^(bool greanted, CFErrorRef error) { //greanted為YES是表示用戶允許,否則為不允許 if (!greanted) { tip=1; } //發送一次信號 dispatch_semaphore_signal(sema); }); //等待信號觸發 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); if (tip) { //我們要在這里做一個提示框提示用戶給了授權才能獲取 return; } //這個是用來獲取數據的 [self getPhoneContactAddress:addBook]; }
//獲取手機通訊錄中的信息
- (void)getPhoneContactAddress:(ABAddressBookRef )addBook { //手機通訊錄獲取存放的數組 self.contactArray = [NSMutableArray array]; //獲取所有聯系人的數組 CFArrayRef allLinkPeople = ABAddressBookCopyArrayOfAllPeople(addBook); //獲取聯系人總數 CFIndex number = ABAddressBookGetPersonCount(addBook); for (int i = 0; i < number; i++) { ABRecordRef people = CFArrayGetValueAtIndex(allLinkPeople, i); //獲取當前聯系人名字 NSStringfirstName=(__bridge NSString *)(ABRecordCopyValue(people, kABPersonFirstNameProperty)); //獲取當前聯系人姓氏 NSString**lastName=(__bridge NSString *)(ABRecordCopyValue(people, kABPersonLastNameProperty)); //獲取當前聯系人中間名 NSString *middleName=(__bridge NSString*)(ABRecordCopyValue(people, kABPersonMiddleNameProperty)); // 獲取聯系人的頭像 NSData *userImage=(__bridge NSData*)(ABPersonCopyImageData(people)); //注意這里的手機號是一個數組, 因為一人可以有很多個手機號, 這里我處理的是一個手機號對應一個姓名, 多個手機號就有多個相同的姓名不同手機號的模型 NSMutableArray * phoneArr = [[NSMutableArray alloc]init]; ABMultiValueRef phones= ABRecordCopyValue(people, kABPersonPhoneProperty); for (NSInteger j=0; j<ABMultiValueGetCount(phones); j++) { [phoneArr addObject:(__bridge NSString *)(ABMultiValueCopyValueAtIndex(phones, j))]; } for (int i = 0; i< phoneArr.count; i++) { ContactBookModel *contactModel = [[ContactBookModel alloc] init];//把名字拼接在一塊 contactModel.name = [NSString stringWithFormat:@"%@%@%@", lastName, middleName, firstName];contactModel.name = [contactModel.name stringByReplacingOccurrencesOfString:@"(null)" withString:@""]; if (contactModel.name == nil || [contactModel.name isEqualToString:@""]) { contactModel.name = @"無姓名"; } contactModel.imageData = userImage; contactModel.telephone = (__bridge NSString *)(ABMultiValueCopyValueAtIndex(phones, i)); contactModel.telephone = [contactModel.telephone stringByReplacingOccurrencesOfString:@"-" withString:@""]; [self.contactArray addObject:contactModel]; } } }
//到這里未知已經把手機號 姓名跟頭像獲取到了, 接著要做的就是把獲取到的處理了, 一般來說獲取的全部聯系人的信息并不會有很長時間, 真正的耗時就是在獲取之后對數組進行處理的過程中
- (void)handleArray { //字典把獲取到的數組分一下組 self.keyContactDic = [NSMutableDictionary dictionary]; self.keyArray = [NSMutableArray array]; for (ContactBookModel *model in self.contactArray) { NSString *lastName; if (![model.name isEqualToString:@""] || model.name == nil) { lastName =[self transform:[model.name substringToIndex:1]]; } NSString *ZIMU = @"^[A-Za-z]"; NSPredicate *regextestA = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", ZIMU]; BOOL result = [regextestA evaluateWithObject:lastName]; if (!result) { lastName = @"#"; } if (![self.keyArray containsObject:lastName]) { NSMutableArray *array = [NSMutableArray array]; [array addObject:model]; [self.keyContactDic setObject:array forKey:lastName]; [self.keyArray addObject:lastName]; } else { NSMutableArray *array = [self.keyContactDic objectForKey:lastName]; [array addObject:model]; // NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"characterName" ascending:YES]; // array = [NSMutableArray arrayWithArray:[array sortedArrayUsingDescriptors:@[descriptor]]]; } } NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:nil ascending:YES]; self.keyArray = [NSMutableArray arrayWithArray:[self.keyArray sortedArrayUsingDescriptors:@[descriptor]]] ; self.searchDic = [NSMutableDictionary dictionary]; [self.searchDic addEntriesFromDictionary:self.keyContactDic]; dispatch_async(dispatch_get_main_queue(), ^{ [self.tableView reloadData]; }); NSMutableArray *array1 = [NSMutableArray array]; for (NSString *key in self.keyContactDic) { NSArray *array = self.searchDic[key]; for (id obj in array) { [array1 addObject:obj]; } } }
//判斷中文還是英文
-(BOOL)IsChinese:(NSString *)str { for(int i=0; i< [str length];i++){ int a = [str characterAtIndex:i]; if( a > 0x4e00 && a < 0x9fff) { return YES; } } return NO; }
//漢子轉換拼音 把漢子轉為拼音進行排序是我遇到的最大的坑, 因為這個造成了二三十秒 卡頓 (大概有四千的通訊錄), 最后求助了一個群里的大神給了一個c文件
- (NSString *)transform:(NSString *)chinese { if ( chinese == nil || [chinese isEqualToString:@""]) { return nil; }
if ([self IsChinese:chinese]) { char cc = pinyinFirstLetter([chinese characterAtIndex:0]); return [NSString stringWithFormat:@"%c",cc ].uppercaseString; } else { return [chinese substringToIndex:1].uppercaseString; } }
寫到這里大概通訊錄的大概就有了, 數組為全部的, 字典是排過序的, 接下來做的就是存入沙盒中, 因為我們不可能每次都過來獲取通訊錄, 我們歸檔模型, 存入cache
model的.h中遵守NSCoding 而在.m中實現
- (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.name forKey:@"name"]; [aCoder encodeObject:self.telephone forKey:@"telephone"]; [aCoder encodeObject:self.lastName forKey:@"lastName"]; [aCoder encodeObject:self.characterName forKey:@"characterName"]; [aCoder encodeObject:self.imageData forKey:@"imageData"]; [aCoder encodeBool:self.isSelect forKey:@"isSelect"]; }
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { if (self = [super init]) { self.name = [aDecoder decodeObjectForKey:@"name"]; self.telephone = [aDecoder decodeObjectForKey:@"telephone"]; self.lastName = [aDecoder decodeObjectForKey:@"lastName"]; self.characterName = [aDecoder decodeObjectForKey:@"characterName"]; self.imageData = [aDecoder decodeObjectForKey:@"imageData"]; self.isSelect = [aDecoder decodeBoolForKey:@"isSelect"]; } return self; }
//下面寫到沙盒中
- (void)writeToSandBox { NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; NSString *conactPath = [cache stringByAppendingPathComponent:@"contactArray.text"]; BOOL exist = [[NSFileManager defaultManager] fileExistsAtPath:conactPath]; if (exist) { BOOL result = [[NSFileManager defaultManager] removeItemAtPath:conactPath error:nil]; if (result) { NSLog(@"移除成功"); } else { NSLog(@"移除失敗"); } }
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //我這里把總的數組存進去了, 因為我要用的只是全部的數組就行, 如果有需要可以把字典也存進去 NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.contactArray]; BOOL result = [data writeToFile:conactPath atomically:YES]; if (result) { NSLog(@"寫入成功"); }else { NSLog(@"寫入失敗"); } }); }
//然后我們可以在用的地方存沙盒中取出來
//還沒有完, 因為我們用戶會修改通訊錄的, 不過不要怕有修改的回調函數, 我們在appdelegate中寫下如下
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //類似于觀察者, 當用戶修改了通訊錄, 我們進入程序會有回調 ABAddressBookRef addresBook = ABAddressBookCreateWithOptions(NULL, NULL); ABAddressBookRegisterExternalChangeCallback(addresBook, addressBookChanged, (__bridge void *)(self)); return YES; }
void addressBookChanged(ABAddressBookRef addressBook, CFDictionaryRef info, void *context) { //這是一個回調函數, 當通訊錄發生改變的時候, 用戶再次進來會有回調, 我們可以在這里做操作, 比如重新讀取通訊錄把老的數據從沙盒中刪除, 然后把新的寫入沙盒 }
//簡書寫文章排版也是一個大問題, 以后好好學下也要, 這樣的排版我自己都不愿意看..... 我看看有沒有上傳文件的地方, 上傳一份最終要的是c文件漢子轉拼音的那個