iOS 獲取系統(tǒng)通訊錄AddressBook的名單遇到的坑

zz做開(kāi)發(fā)已經(jīng)有一段時(shí)間了, 最近換了新公司, 決定寫(xiě)一些東西記錄一下開(kāi)發(fā)中遇到的坑. 新的公司做的是企業(yè)通訊方面的, 首先遇到的坑就是獲取手機(jī)通訊錄的信息.
由于版本適配的問(wèn)題, 這次采用的是AddressBook這個(gè)框架, 這個(gè)框架在iOS9以后已經(jīng)被Contacts所代替. 以后用到這個(gè)在做記錄吧,
廢話(huà)不多說(shuō)了, 這次要做的就是把通訊錄的信息取出來(lái)然后自定義界面展示.第一步要做的就是引入框架頭文件

import <AddressBook/AddressBook.h>

由于訪(fǎng)問(wèn)的是通訊錄要獲得用戶(hù)授權(quán)所有我們要寫(xiě)一個(gè)方法獲取用戶(hù)的授權(quán) 直接貼上代碼吧
新建一個(gè)model類(lèi), 做聯(lián)系人的model
屬性如下, 因?yàn)槲抑恍枰@幾個(gè)所以就只寫(xiě)著幾個(gè)了
#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

下面是獲取通訊錄, 我們可以寫(xiě)一個(gè)類(lèi)專(zhuān)門(mén)處理這個(gè), 因?yàn)樵谕ㄓ嶄浀拈_(kāi)發(fā)中 , 我們可以在不同的控制器里會(huì)用到這個(gè)
-(void)getrightFromUser { //這個(gè)變量用于記錄授權(quán)是否成功遇伞,即用戶(hù)是否允許我們?cè)L問(wèn)通訊錄 int __block tip=0; //聲明一個(gè)通訊簿的引用 ABAddressBookRef addBook =nil; //創(chuàng)建通訊簿的引用 addBook=ABAddressBookCreateWithOptions(NULL, NULL); //創(chuàng)建一個(gè)出事信號(hào)量為0的信號(hào) dispatch_semaphore_t sema=dispatch_semaphore_create(0); //申請(qǐng)?jiān)L問(wèn)權(quán)限 ABAddressBookRequestAccessWithCompletion(addBook, ^(bool greanted, CFErrorRef error) { //greanted為YES是表示用戶(hù)允許月匣,否則為不允許 if (!greanted) { tip=1; } //發(fā)送一次信號(hào) dispatch_semaphore_signal(sema); }); //等待信號(hào)觸發(fā) dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); if (tip) { //我們要在這里做一個(gè)提示框提示用戶(hù)給了授權(quán)才能獲取 return; } //這個(gè)是用來(lái)獲取數(shù)據(jù)的 [self getPhoneContactAddress:addBook]; }
//獲取手機(jī)通訊錄中的信息
- (void)getPhoneContactAddress:(ABAddressBookRef )addBook { //手機(jī)通訊錄獲取存放的數(shù)組 self.contactArray = [NSMutableArray array]; //獲取所有聯(lián)系人的數(shù)組 CFArrayRef allLinkPeople = ABAddressBookCopyArrayOfAllPeople(addBook); //獲取聯(lián)系人總數(shù) CFIndex number = ABAddressBookGetPersonCount(addBook); for (int i = 0; i < number; i++) { ABRecordRef people = CFArrayGetValueAtIndex(allLinkPeople, i); //獲取當(dāng)前聯(lián)系人名字 NSStringfirstName=(__bridge NSString *)(ABRecordCopyValue(people, kABPersonFirstNameProperty)); //獲取當(dāng)前聯(lián)系人姓氏 NSString**lastName=(__bridge NSString *)(ABRecordCopyValue(people, kABPersonLastNameProperty)); //獲取當(dāng)前聯(lián)系人中間名 NSString *middleName=(__bridge NSString*)(ABRecordCopyValue(people, kABPersonMiddleNameProperty)); // 獲取聯(lián)系人的頭像 NSData *userImage=(__bridge NSData*)(ABPersonCopyImageData(people)); //注意這里的手機(jī)號(hào)是一個(gè)數(shù)組, 因?yàn)橐蝗丝梢杂泻芏鄠€(gè)手機(jī)號(hào), 這里我處理的是一個(gè)手機(jī)號(hào)對(duì)應(yīng)一個(gè)姓名, 多個(gè)手機(jī)號(hào)就有多個(gè)相同的姓名不同手機(jī)號(hào)的模型 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 = @"無(wú)姓名"; } contactModel.imageData = userImage; contactModel.telephone = (__bridge NSString *)(ABMultiValueCopyValueAtIndex(phones, i)); contactModel.telephone = [contactModel.telephone stringByReplacingOccurrencesOfString:@"-" withString:@""]; [self.contactArray addObject:contactModel]; } } }
//到這里未知已經(jīng)把手機(jī)號(hào) 姓名跟頭像獲取到了, 接著要做的就是把獲取到的處理了, 一般來(lái)說(shuō)獲取的全部聯(lián)系人的信息并不會(huì)有很長(zhǎng)時(shí)間, 真正的耗時(shí)就是在獲取之后對(duì)數(shù)組進(jìn)行處理的過(guò)程中

- (void)handleArray { //字典把獲取到的數(shù)組分一下組 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; }
//漢子轉(zhuǎn)換拼音 把漢子轉(zhuǎn)為拼音進(jìn)行排序是我遇到的最大的坑, 因?yàn)檫@個(gè)造成了二三十秒 卡頓 (大概有四千的通訊錄), 最后求助了一個(gè)群里的大神給了一個(gè)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; } }

寫(xiě)到這里大概通訊錄的大概就有了, 數(shù)組為全部的, 字典是排過(guò)序的, 接下來(lái)做的就是存入沙盒中, 因?yàn)槲覀儾豢赡苊看味歼^(guò)來(lái)獲取通訊錄, 我們歸檔模型, 存入cache
model的.h中遵守NSCoding 而在.m中實(shí)現(xiàn)
- (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; }
//下面寫(xiě)到沙盒中
- (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), ^{ //我這里把總的數(shù)組存進(jìn)去了, 因?yàn)槲乙玫闹皇侨康臄?shù)組就行, 如果有需要可以把字典也存進(jìn)去 NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.contactArray]; BOOL result = [data writeToFile:conactPath atomically:YES]; if (result) { NSLog(@"寫(xiě)入成功"); }else { NSLog(@"寫(xiě)入失敗"); } }); }

//然后我們可以在用的地方存沙盒中取出來(lái)
//還沒(méi)有完, 因?yàn)槲覀冇脩?hù)會(huì)修改通訊錄的, 不過(guò)不要怕有修改的回調(diào)函數(shù), 我們?cè)赼ppdelegate中寫(xiě)下如下
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //類(lèi)似于觀察者, 當(dāng)用戶(hù)修改了通訊錄, 我們進(jìn)入程序會(huì)有回調(diào) ABAddressBookRef addresBook = ABAddressBookCreateWithOptions(NULL, NULL); ABAddressBookRegisterExternalChangeCallback(addresBook, addressBookChanged, (__bridge void *)(self)); return YES; }
void addressBookChanged(ABAddressBookRef addressBook, CFDictionaryRef info, void *context) { //這是一個(gè)回調(diào)函數(shù), 當(dāng)通訊錄發(fā)生改變的時(shí)候, 用戶(hù)再次進(jìn)來(lái)會(huì)有回調(diào), 我們可以在這里做操作, 比如重新讀取通訊錄把老的數(shù)據(jù)從沙盒中刪除, 然后把新的寫(xiě)入沙盒 }
//簡(jiǎn)書(shū)寫(xiě)文章排版也是一個(gè)大問(wèn)題, 以后好好學(xué)下也要, 這樣的排版我自己都不愿意看..... 我看看有沒(méi)有上傳文件的地方, 上傳一份最終要的是c文件漢子轉(zhuǎn)拼音的那個(gè)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末腥泥,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子乏屯,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矛绘,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡刃永,警方通過(guò)查閱死者的電腦和手機(jī)货矮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)揽碘,“玉大人次屠,你說(shuō)我怎么就攤上這事园匹。” “怎么了劫灶?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵裸违,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我本昏,道長(zhǎng)供汛,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任涌穆,我火速辦了婚禮怔昨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宿稀。我一直安慰自己趁舀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布祝沸。 她就那樣靜靜地躺著矮烹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪罩锐。 梳的紋絲不亂的頭發(fā)上奉狈,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音涩惑,去河邊找鬼仁期。 笑死,一個(gè)胖子當(dāng)著我的面吹牛竭恬,可吹牛的內(nèi)容都是我干的跛蛋。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼痊硕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼问芬!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起寿桨,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤此衅,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后亭螟,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體挡鞍,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年预烙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了墨微。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扁掸,死狀恐怖翘县,靈堂內(nèi)的尸體忽然破棺而出最域,到底是詐尸還是另有隱情,我是刑警寧澤锈麸,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布镀脂,位于F島的核電站,受9級(jí)特大地震影響忘伞,放射性物質(zhì)發(fā)生泄漏薄翅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一氓奈、第九天 我趴在偏房一處隱蔽的房頂上張望翘魄。 院中可真熱鬧,春花似錦舀奶、人聲如沸暑竟。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)光羞。三九已至,卻和暖如春怀大,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呀闻。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工化借, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捡多。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓蓖康,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親垒手。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蒜焊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容