2017-09-07 09:18:50 walkerwqp 阅读数 1004

在ios开发当中,获取用户本地的通讯录功能愈加频繁的出现,七两自己也在自己公司的项目当中遇到的获取本地的通讯录信息的功能(俗称“种子用户功能”,太可怕了)。对此七两总结了自己使用本地通讯录时的注意点,希望对大家有所帮助。

首先对于获取本地通讯录的信息,大苹果在ios9.0之后推出了另外的一个使用方式(controller),类似于之前searchcontroller与searchBar,对此七两在此分成两部分,ios9.0前与ios9.0后两部分进行总结。


一、iOS9.0前

先附上七两自己写得代码:
对于苹果对于通讯录的操作实现可以类比sqlite与sqlite3的管理实现方式,及我们在使用通讯录时需调用类似于sqlite3的管理对象,对此七两自己习惯将对通讯录的操作过程写成单例,具体如下:

.h文件
[objc] view plain copy
  1. #import <Foundation/Foundation.h>  
  2. #import <AddressBook/AddressBook.h>  
  3. #import <UIKit/UIKit.h>  
  4.   
  5. typedef void(^YFAddressBookBlock)(BOOL canRead, ABAuthorizationStatus authorStatus);  
  6.   
  7. @interface YFAddressBookManger : NSObject  
  8.   
  9. @property (nonatomic, assign) ABAddressBookRef addressBook;  
  10. //单例类方法  
  11. + (instancetype)shareManger;  
  12.   
  13. //设置传值  
  14. - (void)canReadAddressBookWithBlock:(YFAddressBookBlock)block;  
  15.   
  16. - (void)gotoSetting:(UIViewController *)vc;  
  17.   
  18. @end  

.m文件

[objc] view plain copy
  1. #import "YFAddressBookManger.h"  
  2.   
  3. @implementation YFAddressBookManger  
  4.   
  5. + (instancetype)shareManger{  
  6.     static YFAddressBookManger *manger = nil;  
  7.   
  8.     static dispatch_once_t onceToken;  
  9.     dispatch_once(&onceToken, ^{  
  10.         if (!manger) {  
  11.             manger = [[YFAddressBookManger alloc]init];  
  12.         }  
  13.     });  
  14.     return manger;  
  15. }  
  16.   
  17. - (id)init{  
  18.     self = [super init];  
  19.     if (self) {  
  20.         _addressBook = ABAddressBookCreateWithOptions(NULLNULL);  
  21.     }  
  22.       
  23.     return self;  
  24. }  
  25.   
  26. //获取读取权限  
  27. - (void)canReadAddressBookWithBlock:(YFAddressBookBlock)block{  
  28.     ABAuthorizationStatus authStatus = ABAddressBookGetAuthorizationStatus();  
  29.     if (authStatus == kABAuthorizationStatusNotDetermined) {  
  30.         ABAddressBookRequestAccessWithCompletion(_addressBook, ^(bool granted, CFErrorRef error) {  
  31.            dispatch_async(dispatch_get_main_queue(), ^{  
  32.                if (error) {  
  33.                    //拒绝访问  
  34.                    block(NO,kABAuthorizationStatusDenied);  
  35.                }else{  
  36.                    block(YES,0);  
  37.                }  
  38.            });  
  39.         });  
  40.     }else if (authStatus == kABAuthorizationStatusAuthorized){  
  41.         block(YES,0);  
  42.     }else{  
  43.         block(NO,authStatus);  
  44.     }  
  45. }  
  46.   
  47. //去设置页面  
  48. - (void)gotoSetting:(UIViewController *)vc{  
  49.     NSString *appName = [[NSBundle mainBundle].infoDictionary valueForKey:@"CFBundleDisplayName"];  
  50.     if (!appName) appName = [[NSBundle mainBundle].infoDictionary valueForKey:@"CFBundleName"];  
  51.     NSString *message = [NSString stringWithFormat:@"请在%@的\"设置-隐私-通讯录\"选项中,\r允许%@访问你的通讯录。",[UIDevice currentDevice].model,appName];  
  52.   
  53.     UIAlertController *alertVC = [[UIAlertController alloc]init];  
  54.     [UIAlertController alertControllerWithTitle:@"提示" message:message preferredStyle:UIAlertControllerStyleAlert];  
  55.     UIAlertAction *sureAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {  
  56.         [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];  
  57.     }];  
  58.     UIAlertAction *cancleAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {  
  59.           
  60.     }];  
  61.     [alertVC addAction:cancleAction];  
  62.     [alertVC addAction:sureAction];  
  63. }  
  64. @end  

后续引用通讯录中得数据至controller中的使用方式如下:
[objc] view plain copy
  1. #pragma mark - 获取本地的通讯录数据  
  2. - (void)getDataSource{  
  3.     if (_dataSource == nil) {  
  4.         _dataSource = [NSMutableArray new];  
  5.     }  
  6.     [[YFAddressBookManger shareManger]canReadAddressBookWithBlock:^(BOOL canRead, ABAuthorizationStatus authorStatus) {  
  7.         if (canRead) {  
  8.             ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULLNULL);  
  9.             CFArrayRef allLinkPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);  
  10.             CFIndex num = ABAddressBookGetPersonCount(addressBook);  
  11.             for (NSInteger i = 0; i < num; i++) {  
  12.                 ABRecordRef  people = CFArrayGetValueAtIndex(allLinkPeople, i);  
  13.                 ABMultiValueRef phones = ABRecordCopyValue(people, kABPersonPhoneProperty);  
  14.                   
  15.                 for (int k = 0; k<ABMultiValueGetCount(phones); k++)  
  16.                 {  
  17.                     //获取該Label下的电话值  
  18.                     NSString * personPhone = (__bridge NSString*)ABMultiValueCopyValueAtIndex(phones, k);  
  19.                       
  20.                     NSString *newP = [personPhone stringByReplacingOccurrencesOfString:@" " withString:@""];  
  21.                     newP = [newP stringByReplacingOccurrencesOfString:@"-" withString:@""];  
  22.                       
  23.                     // 去除+86  
  24.                     if ([newP containsString:@"+86"]) {  
  25.                         newP = [newP stringByReplacingOccurrencesOfString:@"+86" withString:@""];  
  26.                     }  
  27.                     // 去除+  
  28.                     if ([newP containsString:@"+"]) {  
  29.                         newP = [newP stringByReplacingOccurrencesOfString:@"+" withString:@""];  
  30.                     }  
  31.                     if (![_dataSource containsObject:newP]) {  
  32.                         [_dataSource addObject:newP];  
  33.                     }  
  34.                 }  
  35.                   
  36.             }  
  37.   
  38.         }else{  
  39.             [[YFAddressBookManger shareManger] gotoSetting:self];  
  40.         }  
  41.     }];  
  42. }  
如果需要其余的多余的单值属性需不同的字段,代码中的ABRecordCopyValue方法可以理解为C语言中得字典。即可根据不同的单值属性获取。

二、iOS9.0之后

苹果推荐ContactsUI使用,具体的使用方式如下:

[objc] view plain copy
  1. pragma mark - <CNContactPickerViewController代理方法>  
  2. /* 当选中一个联系人时,会执行该方法  
  3. @param picker 选择联系人的控制器  
  4. @param contact 选择的联系人 / 
  5. (void)contactPicker:(CNContactPickerViewController )picker didSelectContact:(CNContact )contact{ 
  6. // 1.获取联系人的姓名   NSString *firstName = contact.givenName; 
  7.   NSString *lastName = contact.familyName; 
  8.   NSLog(@"%@ %@", firstName, lastName); 
  9. // 2.获取联系人的电话号码   NSArray *phoneNumers = contact.phoneNumbers; 
  10.    for (CNLabeledValue *labelValue in phoneNumers) { 
  11.        CNPhoneNumber *phoneNumber = labelValue.value;  
  12.        NSString *phoneValue = phoneNumber.stringValue;  
  13.        NSString *phoneLabel = labelValue.label; 
  14.        NSLog(@"%@ %@", phoneValue, phoneLabel); 
  15.   } 
  16. } 
  17. /* 当选中某一个联系人的某一个属性时,会执行该方法  
  18. @param contactProperty 选中的联系人属性 
  19. */  
  20. (void)contactPicker:(CNContactPickerViewController )picker didSelectContactProperty:(CNContactProperty )contactProperty{}  


具体的细节使用如下:


[objc] view plain copy
  1. // 1.获取授权状态  
  2. CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];  
  3. // 2.如果不是已经授权,则直接返回  
  4. if (status != CNAuthorizationStatusAuthorized) return;  
  5.   
  6. // 3.获取联系人  
  7. // 3.1.创建联系人仓库  
  8. CNContactStore *store = [[CNContactStore alloc] init];  
  9.   
  10. // 3.2.创建联系人的请求对象  
  11. // keys决定这次要获取哪些信息,比如姓名/电话  
  12. NSArray *fetchKeys = @[CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey];  
  13. CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:fetchKeys];  
  14.   
  15. // 3.3.请求联系人  
  16. NSError *error = nil;  
  17. [store enumerateContactsWithFetchRequest:request error:&error usingBlock:^(CNContact * _Nonnull contact, BOOLBOOL * _Nonnull stop) {  
  18.     // stop是决定是否要停止  
  19.     // 1.获取姓名  
  20.     NSString *firstname = contact.givenName;  
  21.     NSString *lastname = contact.familyName;  
  22.     NSLog(@"%@ %@", firstname, lastname);  
  23.   
  24.     // 2.获取电话号码  
  25.     NSArray *phones = contact.phoneNumbers;  
  26.   
  27.     // 3.遍历电话号码  
  28.     for (CNLabeledValue *labelValue in phones) {  
  29.         CNPhoneNumber *phoneNumber = labelValue.value;  
  30.         NSLog(@"%@ %@", phoneNumber.stringValue, labelValue.label);  
  31.     }  
  32. }];  
2016-08-23 11:43:04 bitcser 阅读数 2411

在APP中我们有时候会用到保存手机电话到通讯录的功能,下面代码是实现该功能的DEMO

1第一步:

在工程设置的Build Phases中的Link Binary With Libraries里添加两个依赖库 Contacts.framework  ContactsUI.framework

2导入 :

#import <ContactsUI/CNContactViewController.h>
#import <ContactsUI/CNContactPickerViewController.h>

3准守两个代理:

CNContactViewControllerDelegate,CNContactPickerDelegate

4具体实现代码:

#import "ViewController.h"

@interface ViewController (){
}
@property(nonatomic,strong)CNContactStore *contactStore;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}
- (IBAction)newContactClicked:(id)sender {
    [self saveNewContact];
}
- (IBAction)currentContactClicked:(id)sender {
    [self saveExistContact];
}

- (void)saveNewContact{
    //1.创建Contact对象,必须是可变的
    CNMutableContact *contact = [[CNMutableContact alloc] init];
    //2.为contact赋值,setValue4Contact中会给出常用值的对应关系
    [self setValue4Contact:contact existContect:NO];
    //3.创建新建好友页面
    CNContactViewController *controller = [CNContactViewController viewControllerForNewContact:contact];
    //代理内容根据自己需要实现
    controller.delegate = self;
    //4.跳转
    UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController:controller];
    [self presentViewController:navigation animated:YES completion:^{
        
    }];
    
}

//保存现有联系人实现
- (void)saveExistContact{
    //1.跳转到联系人选择页面,注意这里没有使用UINavigationController
    CNContactPickerViewController *controller = [[CNContactPickerViewController alloc] init];
    controller.delegate = self;
    [self presentViewController:controller animated:YES completion:^{
        
    }];
}

#pragma mark - CNContactViewControllerDelegate
- (void)contactViewController:(CNContactViewController *)viewController didCompleteWithContact:(nullable CNContact *)contact{
    [viewController dismissViewControllerAnimated:YES completion:^{
        
    }];
}
#pragma mark - CNContactPickerDelegate
//2.实现点选的代理,其他代理方法根据自己需求实现
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact{
    [picker dismissViewControllerAnimated:YES completion:^{
        //3.copy一份可写的Contact对象,不要尝试alloc一类,mutableCopy独此一家
        CNMutableContact *c = [contact mutableCopy];
        //4.为contact赋值
        [self setValue4Contact:c existContect:YES];
        //5.跳转到新建联系人页面
        CNContactViewController *controller = [CNContactViewController viewControllerForNewContact:c];
        controller.delegate = self;
        UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController:controller];
        [self presentViewController:navigation animated:YES completion:^{
        }];
    }];
}

//设置要保存的contact对象
- (void)setValue4Contact:(CNMutableContact *)contact existContect:(BOOL)exist{
    if (!exist) {
        //名字和头像
        contact.nickname = @"oriccheng";
        //        UIImage *logo = [UIImage imageNamed:@"..."];
        //        NSData *dataRef = UIImagePNGRepresentation(logo);
        //        contact.imageData = dataRef;
    }
    //电话,每一个CNLabeledValue都是有讲究的,如何批评,可以在头文件里面查找,这里是给出几个常用的
    CNLabeledValue *phoneNumber = [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberMobile value:[CNPhoneNumber phoneNumberWithStringValue:@"18888888888"]];
    if (!exist) {
        contact.phoneNumbers = @[phoneNumber];
    }
    //现有联系人情况
    else{
        if ([contact.phoneNumbers count] >0) {
            NSMutableArray *phoneNumbers = [[NSMutableArray alloc] initWithArray:contact.phoneNumbers];
            [phoneNumbers addObject:phoneNumber];
            contact.phoneNumbers = phoneNumbers;
        }else{
            contact.phoneNumbers = @[phoneNumber];
        }
    }
    
    //网址:CNLabeledValue *url = [CNLabeledValue labeledValueWithLabel:@"" value:@""];
    //邮箱:CNLabeledValue *mail = [CNLabeledValue labeledValueWithLabel:CNLabelWork value:self.poiData4Save.mail];
    
    //特别说一个地址,PostalAddress对应的才是地址
    CNMutablePostalAddress *address = [[CNMutablePostalAddress alloc] init];
    address.state = @"辽宁省";
    address.city = @"沈阳市";
    address.postalCode = @"111111";
    //外国人好像都不强调区的概念,和具体地址拼到一起
    address.street = @"沈河区惠工街10号";
    //生成的上面地址的CNLabeledValue,其中可以设置类型CNLabelWork等等
    CNLabeledValue *addressLabel = [CNLabeledValue labeledValueWithLabel:CNLabelWork value:address];
    if (!exist) {
        contact.postalAddresses = @[addressLabel];
    }else{
        if ([contact.postalAddresses count] >0) {
            NSMutableArray *addresses = [[NSMutableArray alloc] initWithArray:contact.postalAddresses];
            [addresses addObject:addressLabel];
            contact.postalAddresses = addresses;
        }else{
            contact.postalAddresses = @[addressLabel];
        }
    }
}



@end



2017-03-23 16:06:08 Samson_Shu 阅读数 356

参考了几个大神的文档后,根据自己这边的需要,觉得大家可能也有可能会需要就整理了这篇文章。

主要分为三大步骤:

链接ios原生库

编写C++类调用ios原生通讯录

qml调用封装的C++类(调通讯录的类)


链接ios原生库
在QT工程pro配置文件中加上如下代码:

这里得特别注意的是,你要调用的ios原生函数或方法是出自哪个类库的,一定要全部添加进去,要不编译就会提示找不到你写的ios函数或方法,另外要讲你写的mm文件路径添加进去

ios
{
   LIBS += -framework Foundation -framework UIKit -framework Contacts -framework ContactsUI
   OBJECTIVE_SOURCES += \
        SamsonQt_IOS.mm
}

编写C++类调用ios原生通讯录

.h 文件中
*这里是c++头文件,由于是配合qml使用,所以信号槽你得换个思路,也就是c++类要给qml返回一个收到结果的信号,而qml调用c++则相当qml发了一个打开的信号给c++类,c++这边就是接收打开这个信号的槽,这个槽里面在去打开ios原生的东西*
 #include <QObject>
#include <QString>
class SamsonQt_IOS : public QObject
{
    Q_OBJECT
public:
    static SamsonQt_IOS *s_contactsPicker;

signals:
//    void contactSelected(const QString nameString);
      void returnContactInfo(QString imgpath);
public slots:
    void show(void);
//    void contactSelected(const QString nameString);
};

#endif // SAMSONQT_IOS_H

.mm
  *这里其实相当于写ios原生代码,没有做过ios原生的童鞋,可以网上百度ios的对应代码就可以了。这里相当在mm里面吧ios对应的头文件与实现文件放在了一起。*
#import <Foundation/Foundation.h>
#import <Contacts/Contacts.h>
#import <ContactsUI/ContactsUI.h>
#import <UIKit/UIKit.h>
#import <ContactsUI/CNContactPickerViewController.h>


@interface ViewController : UIViewController<CNContactPickerDelegate,CNContactViewControllerDelegate>
{
    CNContactPickerViewController *picker;
}
@end

@implementation ViewController

-(void)showContactPicker
{
    picker = [[CNContactPickerViewController alloc] init];
    [picker setDelegate:(id)self];
    UIViewController *rootCtrl = [UIApplication sharedApplication].keyWindow.rootViewController;

    [rootCtrl presentViewController:picker animated:YES completion:nil];
}

-(void)contactPickerDidCancel:(CNContactPickerViewController *)picker
{
    NSLog(@"取消选择联系人");
}

-(void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact
{
    NSString *lastname = contact.familyName;
    NSString *firstname = contact.givenName;

    NSString *fullNameStr = [NSString stringWithFormat:@"%@%@",lastname,firstname];
    NSLog(@"%@",fullNameStr);
//重点在这里:将原生代理返回的值转换为对应qt里面的值,这里是将nsstring 转换为了Qstring,具体如何操作,可查看qtapi或度娘.转换好以后,就是发送收到结果的信号给需要的地方了。其实和qt的c++使用没什么区别
    QString fullName = QString::fromNSString(fullNameStr);
//    emit SamsonQt_IOS::s_contactsPicker->contactSelected(fullName);
    emit SamsonQt_IOS::s_contactsPicker->returnContactInfo(fullName);
    SamsonQt_IOS::s_contactsPicker = NULL;
}
@end

//这里可千万不要忘记是c++类,需要加上构造函数或实例,并且实现其槽方法,这里同时也是打开ios原生的信号。
SamsonQt_IOS *SamsonQt_IOS::s_contactsPicker = NULL;

void SamsonQt_IOS::show(void)
{
    SamsonQt_IOS::s_contactsPicker = this;
    这里是用id强转后充当原生controller来使用,更深入的原理,大家可以自行研究。
    void *context = [[ViewController alloc]init];
    [(id)context showContactPicker];
}

文章到这里就结束了。有可能还有一些人会不明白,具体的需要大家对跨平台调用的原理弄明白弄懂,这样才会更加容易上手一些。源码地址在下面,可以下载后自己多运行多读代码来自己体会。谢谢。

下载地址:http://download.csdn.net/detail/samson_shu/9790918

2016-10-21 16:29:26 MiaoMiaoDog 阅读数 1250

    iOS10出来以后  为了适配  项目整个动了很多  比如极光推送整个换了一遍  还有各种请求权限的修改和添加  简直日了狗了  不过辛亏网上大神多  一些基本的问题都有解答  但是通讯录是很坑的  因为iOS10 做了很大的改动  让我们一起来看看有哪些坑

  1.权限问题  

  iOS10之后需要在info plist文件添加新的键值对 来保证我们能访问系统的功能  如果不添加的话直接会闪退掉 

  访问通讯录的话 我们需要添加Privacy - Contacts Usage Description  后面的值为字符串  即请求获取权限时显示的请求信息  比如我们项目中添加的是 我们需要使用您的通讯录   当然其他的  比如 相册  摄像头 话筒都一样  

  2.调取通讯录的方法 

 上网搜了很久  好像没有发现有人特别将iOS10之后修改了调取通讯录的方法  所以导致我一直以为调取通讯录奔溃是因为权限问题 

 不过还好找到  iOS9 以前一直调取的是  ABPeoplePickerNavigationController  iOS9以后改为CNContactPickerViewController

并且废弃了 AddressBook  AddressBookUI框架  换为ContactsUI框架 所以当我们在iOS10  调用iOS9以下的方法时 不会显示请求权限信息 但是回调的时候会崩溃掉 


 自己写了个demo  放在github上https://github.com/DogWangWangWang/CH_GetContactInfor 如果有不明白的地方 欢迎大家下载 
 

  


  


2016-03-14 19:15:12 wyubaoyuan 阅读数 1712

IOS监听手机通讯录变化

做类似微信通过手机联系人添加好友时,为了减少网络请求,我们一般做法是,第一次请求对比完后缓存起来,监听通讯录是否有变化,有变化再去请求网络对比,

  • 1、通讯录编程首先在项目中导入库文件AddressBook.framework、AddressBookUI.framework
  • 2、在相应的类中导入头文件#import <AddressBook/AddressBook.h>
    \#import <AddressBookUI/AddressBookUI.h>
  • 3、在AppDelegate.m中的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions这个方法中添加监听 代码如下:
  • //监听通讯录变化
    ABAddressBookRef addresBook = ABAddressBookCreateWithOptions(NULL, NULL);
    ABAddressBookRegisterExternalChangeCallback(addresBook, addressBookChanged, (__bridge void *)(self));
  • 4、添加监听方法:
  • `//监听通讯录变化
    void addressBookChanged(ABAddressBookRef addressBook, CFDictionaryRef info, void *context)
    {
    NSLog(@”通讯录有变化”);

}`

没有更多推荐了,返回首页