呐呐,谈个女朋友想解锁更多姿势你也知道必须是要有权限访问的,强制执行?大哥,别闹,违法的!!!
今天就来说说iOS中关于权限的那些事!
- 相机权限:
Privacy - Camera Usage Description
- 相册权限:
Privacy - Photo Library Usage Description
- 定位权限:
Privacy - Location Always and When In Use Usage Description
- 定位权限:
Privacy - Location When In Use Usage Description
- 蓝牙权限:
Privacy - Bluetooth Peripheral Usage Description
- 通讯录权限:
Privacy - Contacts Usage Description
- 麦克风权限:
Privacy - Microphone Usage Description
一 : 通讯录权限
iOS 9 以前的通讯录框架
-
AddressBookUI.framework
框架- 提供了联系人列表界面、联系人详情界面、添加联系人界面等
- 一般用于选择联系人
-
AddressBook.framework
框架- 纯 C 语言的 API,仅仅是获得联系人数据
- 没有提供 UI 界面展示,需要自己搭建联系人展示界面
- 里面的数据类型大部分基于 Core Foundation 框架,使用起来炒鸡复杂
iOS 9 以后最新通讯录框架
-
ContactsUI.framework
框架。拥有 AddressBookUI.framework 框架的所有功能,使用起来更加的面向对象。
-
Contacts.framework
框架。拥有 AddressBook.framework 框架的所有功能,不再是 C 语言的 API,使用起来非常简单。
WechatIMG1176.png值得注意的是如果只是单纯的选择某个联系人的信息时使用AddressBookUI.framework或者ContactsUI.framework 框架时是
不需要设置权限
的
只有在获取用户通讯录的时候系统才会弹出权限提示框
选择亲近的联系人只是获取单个联系人信息,这里不需要设置权限相关
1. iOS 9 之前使用AddressBookUI.framework
框架获取联系人信息
- (void) pushToEarlyIOS9VC
{
ABPeoplePickerNavigationController *pvc = [[ABPeoplePickerNavigationController alloc] init];
pvc.peoplePickerDelegate = self;
[self presentViewController:pvc animated:YES completion:nil];
}
这样就跳转到了联系人界面,但是这里又分为两种情况
,
一种是点击联系人后直接回調:
代码如下:
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person
{
CFStringRef firstNameRef = ABRecordCopyValue(person, kABPersonFirstNameProperty);
CFStringRef lastNameRef = ABRecordCopyValue(person, kABPersonLastNameProperty);
NSString * firstName = CFBridgingRelease(firstNameRef);
NSString * lastName = CFBridgingRelease(lastNameRef);
NSString *nameStr;
if (! firstName && lastName) {
nameStr = lastName;
}else if (! lastName && firstName){
nameStr = firstName;
}else if (firstName && lastName){
nameStr = [NSString stringWithFormat:@"%@%@", lastName, firstName];
}else{
nameStr = @"";
}
ABMultiValueRef phoneMulti = ABRecordCopyValue(person, kABPersonPhoneProperty);
CFIndex count = ABMultiValueGetCount(phoneMulti);
NSString *phoneNum;
for (int i = 0; i < count; i++)
{
// label:_$!<Mobile>!$_或者_$!<Home>!$_
NSString *label = (__bridge_transfer NSString *)ABMultiValueCopyLabelAtIndex(multi, i);
// _$!<Mobile>!$_或者_$!<Home>!$_对应的手机号码字符串
NSString *phone =(__bridge_transfer NSString *) ABMultiValueCopyValueAtIndex(multi, i);//手机号
NSLog(@"%@---%@", label, phone);
phoneNum = phone;
}
// 之后对取到的手机号phoneNum进行字符串剪切、替换、文本赋值操作
}
另一种是点击联系人后跳转到详情页
再进行号码选择:
代码如下:
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
SXLog(@"%d---%d",property,identifier);
// 获取该联系人多重属性--电话号
ABMutableMultiValueRef phoneMulti = ABRecordCopyValue(person, kABPersonPhoneProperty);
// 获取该联系人的名字,简单属性,只需ABRecordCopyValue取一次值
ABMutableMultiValueRef firstNameRef = ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString *firstName = (__bridge NSString *)(firstNameRef);
ABMutableMultiValueRef lastNameRef = ABRecordCopyValue(person, kABPersonLastNameProperty);
NSString *lastName = (__bridge NSString *)(lastNameRef);
NSString *nameStr;
if (!firstName && lastName) {
nameStr = lastName;
}else if (!lastName && firstName){
nameStr = firstName;
}else if (firstName && lastName){
nameStr = [NSString stringWithFormat:@"%@%@", lastName, firstName];
}else{
nameStr = @"";
}
// 点击某个联系人电话后dismiss联系人控制器,并回调点击的数据
[self dismissViewControllerAnimated:YES completion:^{
// 从联系人详情中取值,参数identifier是取点击的索引标志(住宅号码、手机号码等)
NSString *phoneNum = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(phoneMulti, ABMultiValueGetIndexForIdentifier(phoneMulti,identifier)) ;
// 之后对取到的手机号phoneNum进行字符串剪切、替换、文本赋值操作
}];
}
2. iOS 9 之后使用ContactsUI.framework
框架获取联系人信息
- (void) pushToAfterIOS9VC
{
if (@available(iOS 9.0, *)) {
CNContactPickerViewController *contactPickerViewController = [[CNContactPickerViewController alloc] init];
contactPickerViewController.delegate = self;
[self presentViewController:contactPickerViewController animated:YES completion:nil];
} else {
// Fallback on earlier versions
}
}
这里也跟使用AddressBookUI.framework
一样,分两种情况,点击联系人后直接回調
:
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact {
NSString *givenName = contact.givenName;
NSString *familyName = contact.familyName;
NSArray * phoneNumbers = contact.phoneNumbers;
for (CNLabeledValue<CNPhoneNumber*>*phone in phoneNumbers) {
//homeOrMobile:遍历个人对应所有手机号得到的值(_$!<Mobile>!$_或者_$!<Home>!$_)
NSString *homeOrMobile = phone.label;
CNPhoneNumber *phonNumber = (CNPhoneNumber *)phone.value;
// 手机号字符串
NSString *phoneNumberString = phonNumber.stringValue;
// 之后做字符串的剪切、替换、文本赋值等操作
}
}
点击联系人后跳转到进入详情页
进行选择:
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty {
NSString *givenName = contactProperty.contact.givenName;
NSString *familyName = contactProperty.contact.familyName;
NSArray * phoneNumbers = contactProperty.contact.phoneNumbers;
for (CNLabeledValue<CNPhoneNumber*>*phone in phoneNumbers) {
//homeOrMobile:遍历详情属性得到的值(_$!<Mobile>!$_或者_$!<Home>!$_)
NSString *homeOrMobile = phone.label;
CNPhoneNumber *phonNumber = (CNPhoneNumber *)phone.value;
// 手机号字符串
NSString *phoneNumberString = phonNumber.stringValue;
// 如果不做此判断取到的手机号都是最后一个(一个人可能对应多个手机号)
if ([homeOrMobile isEqualToString:contactProperty.label]) {
// contactProperty.label手动选择的结果(_$!<Mobile>!$_或者_$!<Home>!$_其中之一)
// 匹配之后做字符串的剪切、替换、文本赋值等操作
}
}
}
到这里选择联系人的几种情况就讲完了,值得注意的是单纯的选择某个联系人的信息跟权限是无关的,是无关的,是无关的!!!
只有在你想获取用户通讯录的时候才需设置用户权限
获取用户通讯录
if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0){
[self requestContactAuthorAfterSystemVersion9];
}else{
[self requestContactAuthorEarlySystemVersion9];
}
3. iOS9之前使用AddressBook.framework
框架获取用户通讯录
- (void) requestContactAuthorEarlySystemVersion9{
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined){
ABAddressBookRef bookRef = ABAddressBookCreate();
ABAddressBookRequestAccessWithCompletion(bookRef, ^(bool granted, CFErrorRef error) {
if (granted){
bookIsOpen = YES;
[self getADBookEarlyIOS9];
}else{
bookIsOpen = NO;
}
});
}else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized){
bookIsOpen = YES;
[self getADBookEarlyIOS9];
}
}
- (void) getADBookEarlyIOS9{
// 2. 获取所有联系人
ABAddressBookRef addressBookRef = ABAddressBookCreate();
CFArrayRef arrayRef = ABAddressBookCopyArrayOfAllPeople(addressBookRef);
long count = CFArrayGetCount(arrayRef);
for (int i = 0; i < count; i++) {
NSMutableDictionary *dic = [NSMutableDictionary new];
//获取联系人对象的引用
ABRecordRef people = CFArrayGetValueAtIndex(arrayRef, i);
//获取当前联系人名字
NSString *firstName=(__bridge NSString *)(ABRecordCopyValue(people, kABPersonFirstNameProperty));
//获取当前联系人姓氏
NSString *lastName=(__bridge NSString *)(ABRecordCopyValue(people, kABPersonLastNameProperty));
//拼接姓名
NSString *nameStr;
if (!lastName && !firstName) {
nameStr = @"";
}else if (!lastName && firstName){
nameStr = [NSString stringWithFormat:@"%@",firstName];
}else if (lastName && !firstName){
nameStr = [NSString stringWithFormat:@"%@",lastName];
}else if (lastName && firstName){
nameStr = [NSString stringWithFormat:@"%@%@",lastName,firstName];
}
NSString *phoneStr;
ABMultiValueRef phones = ABRecordCopyValue(people, kABPersonPhoneProperty);
for (NSInteger j=0; j<ABMultiValueGetCount(phones); j++) {
NSString *phone = (__bridge NSString *)(ABMultiValueCopyValueAtIndex(phones, j));
phoneStr = phone;
}
if (!phoneStr) {
phoneStr = @"";
}
[dic setObject:phoneStr forKey:@"mobile"];
[dic setObject:nameStr forKey:@"name"];
if ([self isBlankString:nameStr] || [self isBlankString:phoneStr]) {
//名字或者手机号为空视为非法
}else{
[self.phoneNumArr addObject: dic];
}
}
}
4. iOS9之后使用Contacts.framework
框架获取用户通讯录
- (void)requestContactAuthorAfterSystemVersion9{
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
if (status == CNAuthorizationStatusNotDetermined) {
CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError* _Nullable error) {
if (granted) {
bookIsOpen = YES;
[self getADBookAfterIOS9];
}else{
bookIsOpen = NO;
[self showAlertVCWithTitle:@"通讯录访问" message:@"去分期需要访问您的通讯录"];
}
}];
}else if(status == CNAuthorizationStatusRestricted){
bookIsOpen = NO;
[self showAlertVCWithTitle:@"通讯录访问" message:@"去分期需要访问您的通讯录"];
}else if (status == CNAuthorizationStatusDenied){
bookIsOpen = NO;
[self showAlertVCWithTitle:@"通讯录访问" message:@"去分期需要访问您的通讯录"];
}else if (status == CNAuthorizationStatusAuthorized){//已经授权
bookIsOpen = YES;
//有通讯录权限-- 进行下一步操作
[self getADBookAfterIOS9];
}
}
- (void) showAlertVCWithTitle:(NSString *)title message:(NSString *)message{
UIAlertController *alertC = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *cancleAC = [UIAlertAction actionWithTitle:@"取消" style:(UIAlertActionStyleCancel) handler:nil];
UIAlertAction *openAC = [UIAlertAction actionWithTitle:@"打开" style:(UIAlertActionStyleDestructive) handler:^(UIAlertAction * _Nonnull action) {
NSURL *settingUrl = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if ([[UIApplication sharedApplication] canOpenURL:settingUrl]) {
[[UIApplication sharedApplication] openURL:settingUrl];
}
}];
[alertC addAction:cancleAC];
[alertC addAction:openAC];
[self presentViewController:alertC animated:YES completion:nil];
}
- (void) getADBookAfterIOS9{
// 获取指定的字段,并不是要获取所有字段,需要指定具体的字段
NSArray *keysToFetch = @[CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey];
CNContactFetchRequest *fetchRequest = [[CNContactFetchRequest alloc] initWithKeysToFetch:keysToFetch];
CNContactStore *contactStore = [[CNContactStore alloc] init];
[contactStore enumerateContactsWithFetchRequest:fetchRequest error:nil usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {
NSMutableDictionary *mDic = [NSMutableDictionary new];
NSString *givenName = contact.givenName;
NSString *familyName = contact.familyName;//姓
//拼接姓名
NSString *nameStr;
if (!givenName && !familyName) {
nameStr = @"";
}else if (!givenName && familyName){
nameStr = [NSString stringWithFormat:@"%@",familyName];
}else if (givenName && !familyName){
nameStr = [NSString stringWithFormat:@"%@",givenName];
}else if (givenName && familyName){
nameStr = [NSString stringWithFormat:@"%@%@",familyName,givenName];
}
NSArray *phoneNumbers = contact.phoneNumbers;
NSString *phoneStr;
for (CNLabeledValue *labelValue in phoneNumbers) {
//遍历一个人名下的多个电话号码
NSString *label = labelValue.label;
CNPhoneNumber *phoneNumber = labelValue.value;
NSString * string = phoneNumber.stringValue ;
//去掉电话中的特殊字符
//string = [string stringByReplacingOccurrencesOfString:@"+86" withString:@""];
//string = [string stringByReplacingOccurrencesOfString:@"-" withString:@""];
//string = [string stringByReplacingOccurrencesOfString:@"(" withString:@""];
//string = [string stringByReplacingOccurrencesOfString:@")" withString:@""];
//string = [string stringByReplacingOccurrencesOfString:@" " withString:@""];
phoneStr = string;
}
if (!phoneStr) {
phoneStr = @"";
}
[mDic setObject:phoneStr forKey:@"mobile"];
[mDic setObject:nameStr forKey:@"name"];
if ([self isBlankString:nameStr] || [self isBlankString:phoneStr]) {
//名字或者手机号为空视为非法
}else{
[self.phoneNumArr addObject:mDic];
}
}];
}
二:位置权限
导入类库#import <CoreLocation/CLLocationManager.h>
并遵守CLLocationManagerDelegate
协议
#pragma mark - 启动定位权限
-(void)getLocation
{
//判断定位功能是否打开
if ([CLLocationManager locationServicesEnabled]) {
locationmanager = [[CLLocationManager alloc]init];
locationmanager.delegate = self;
currentCity = [NSString new];
if (@available(iOS 9.0, *)) {
locationmanager.allowsBackgroundLocationUpdates = YES;
} else {
// Fallback on earlier versions
}
[locationmanager requestAlwaysAuthorization];//后台始终定位
[locationmanager requestWhenInUseAuthorization];//使用期间定位
//设置寻址精度
locationmanager.desiredAccuracy = kCLLocationAccuracyBest;
locationmanager.distanceFilter = 5.0;
[locationmanager startUpdatingLocation];
}
}
#pragma mark CoreLocation delegate (定位失败)
//定位失败后调用此代理方法
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
//失败后执行你需要的相应操作
strlatitude = @"";
strlongitude = @"";
}
#pragma mark 定位成功后则执行此代理方法
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
[locationmanager stopUpdatingHeading];
//旧址
CLLocation *currentLocation = [locations lastObject];
CLGeocoder *geoCoder = [[CLGeocoder alloc]init];
strlatitude = [NSString stringWithFormat:@"%.5f",currentLocation.coordinate.latitude];
strlongitude = [NSString stringWithFormat:@"%.5f",currentLocation.coordinate.longitude];
SXLog(@"%@---%@",strlatitude,strlongitude);
//反地理编码
[geoCoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
if (placemarks.count > 0) {
CLPlacemark *placeMark = placemarks[0];
currentCity = placeMark.locality;
if (!currentCity) {
currentCity = @"无法定位当前城市";
}
//placeMark里面包含了你需要的信息,做相应处理就好了
}
}];
}
如果有更高需求,可点进协议查看更多方法
至于高德、百度地图,之前也都用过,官方文档说的也比较清晰,这里就不过多解读了
这里还需要注意的是如果同时设置了requestWhenInUseAuthorization
和requestAlwaysAuthorization
,requestAlwaysAuthorization
的权限是大于(包括)requestWhenInUseAuthorization
的
如果需要后台持续定位,首先要将下图的Location updates打钩
屏幕快照 2018-08.png//执行如下方法
[self.locationManager requestAlwaysAuthorization];
这样就可以实现后台定位,但是该方法只能实现后台定位20-30分钟的时间;
如果想达到后台永久持续定位的效果,在定位的时候需要添加如下代码:
self.locationManager.pausesLocationUpdatesAutomatically = NO; //系统是否可以自行中断程序的定位功能
该方法让系统不能够自行关闭程序的定位功能,保证程序一直处于后台定位中
三:相册权限
相机、相册、麦克风等权限状态都对应下列这种
AuthorizationStatusNotDetermined // 用户从未进行过授权等处理,首次访问相应内容会提示用户进行授权
AuthorizationStatusAuthorized = 0, // 用户已授权,允许访问
AuthorizationStatusDenied, // 用户拒绝访问
AuthorizationStatusRestricted, // 应用没有相关权限,且当前用户无法改变这个权限,比如:家长控制
是否支持
[UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]
获取权限状态
ios8之前
ALAuthorizationStatus status = [ALAssetsLibrary authorizationStatus];
iOS8之后
#import <Photos/PHPhotoLibrary.h>
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
请求权限
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status == PHAuthorizationStatusAuthorized){
//已授权
}else{
//未授权
}
}];
这里值得注意的一点是
1、iOS11之前访问相册和存储照片到相册(读写权限),需要用户授权,需要添加NSPhotoLibraryUsageDescription
2、iOS11之后:默认开启访问相册权限(读权限),无需用户授权,无需添加NSPhotoLibraryUsageDescription,添加图片到相册(写权限),才需要用户授权,需要添加NSPhotoLibraryAddUsageDescription
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
if (status == PHAuthorizationStatusRestricted || status == PHAuthorizationStatusDenied){
// 无权限
UIAlertView * alart = [[UIAlertView alloc]initWithTitle:@"温馨提示" message:@"请您设置允许该应用访问您的相机\n设置>隐私>相机" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
[alart show];
return;
}
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status == PHAuthorizationStatusRestricted || status == PHAuthorizationStatusDenied) {
SXLog(@"没权限");
}else if (status == PHAuthorizationStatusNotDetermined){
SXLog(@"暂未确定");
}else if (status == PHAuthorizationStatusAuthorized){
SXLog(@"已授权");
}
}];
四:相机、麦克风权限
是否支持
// 是否有摄像头
[UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]
// 前置摄像头是否可用
[UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront];
// 后置摄像头是否可用
[UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear];
获取权限状态
//获取相机权限状态
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
//获取麦克风权限状态
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
获取相机、麦克风权限
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {
//相机授权成功
}else{
//相机授权失败
}
}];
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
if (granted) {
//麦克风授权成功
}else{
//麦克风授权失败
}
}];
#import <Foundation/Foundation.h>
//获取相机或麦克风权限状态
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (status == AVAuthorizationStatusNotDetermined) {
//暂未授权的处理, AVMediaTypeVideo改为AVMediaTypeAudio就是麦克风
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {
SXLog(@"相机或麦克风授权成功");
}else{
SXLog(@"相机或麦克风授权失败");
}
}];
}else if (status == AVAuthorizationStatusRestricted || status == AVAuthorizationStatusDenied){
SXLog(@"无权限");
}else if (status == AVAuthorizationStatusAuthorized){
SXLog(@"有权限");
}
五:推送权限
这个最近几个项目用的都是极光或者友盟,就不多说了,按文档无脑集成就好!
网友评论