一个 APP 需要使用各种权限,比如相机,相册,位置等等,一般情况下我们习惯用到的时候,就去判断权限然后调用系统的设备来使用.这样也符合苹果的安全特点.
但是,让我们在编程过程中,会发现,如果事先不知道是否已经请求过权限了,那么就需要在使用之前先判断是否有权限,判断完成之后,等待用户赋予权限,然后继续执行.
拿通讯录举例来说,假设我们要打开系统的通讯录列表,要这么写
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];//等待授权状态if (status == CNAuthorizationStatusNotDetermined) {CNContactStore *store = [[CNContactStore alloc] init];[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError* _Nullable error) {granted?[self openContact]:[JDMessageView showMessage:@"无法读取通讯录"];}];}//应用程序未被授权访问联系人数据。*用户不能更改该应用程序的状态,可能由于活跃的限制,如家长控制else{if(status == CNAuthorizationStatusRestricted)[JDMessageView showMessage:@"无权访问"];/* !明确拒绝用户访问联系人数据的应用程序。*/else if (status == CNAuthorizationStatusDenied)[JDMessageView showMessage:@"无访问权限"];else if (status == CNAuthorizationStatusAuthorized)[self openContact];}
打开系统通讯录列表方法
- (void)openContact
{CNContactPickerViewController *pickerVC =[[CNContactPickerViewController alloc] init];pickerVC.delegate = self;[self presentViewController:pickerVC animated:YES completion:nil];
}
如果我们要在写一个获取通讯录列表的方法,那么必须要在写这样一个方法
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];//等待授权状态if (status == CNAuthorizationStatusNotDetermined) {CNContactStore *store = [[CNContactStore alloc] init];[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError* _Nullable error) {granted?[self getContactsArrayList]):[JDMessageView showMessage:@"无法读取通讯录"];}];}/*!应用程序未被授权访问联系人数据。*用户不能更改该应用程序的状态,可能由于活跃的限制,如家长控制。*/else{if(status == CNAuthorizationStatusRestricted)[JDMessageView showMessage:@"无权访问"];/* !明确拒绝用户访问联系人数据的应用程序。*/else if (status == CNAuthorizationStatusDenied)[JDMessageView showMessage:@"无访问权限"];else if (status == CNAuthorizationStatusAuthorized)[self getContactsArrayList];}
有人说这两个方法,完全可以通过一个标志简化下代码,这样写:
BOOL isSystemUI ;//是否是打开系统通讯录页面CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];//等待授权状态if (status == CNAuthorizationStatusNotDetermined) {CNContactStore *store = [[CNContactStore alloc] init];[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError* _Nullable error) {granted?(_isSystemUI?[self openContact]:[self getContactsArrayList]):[JDMessageView showMessage:@"无法读取通讯录"];}];}/*!应用程序未被授权访问联系人数据。*用户不能更改该应用程序的状态,可能由于活跃的限制,如家长控制。*/else{if(status == CNAuthorizationStatusRestricted)[JDMessageView showMessage:@"无权访问"];/* !明确拒绝用户访问联系人数据的应用程序。*/else if (status == CNAuthorizationStatusDenied)[JDMessageView showMessage:@"无访问权限"];else if (status == CNAuthorizationStatusAuthorized)_isSystemUI?[self openContact]:[self getContactsArrayList];}
但是假如我又加一个添加联系人方法呢?因为你无法判断他是需要先看列表还是先添加联系人,所以你就必须每次都得判断一下权限.这对于我们变成和封装方法是一个非常痛苦的.
所以很多 APP 都采用,第一次安装就把各种权限请求一遍的策略.我觉得就是为了两点:
1.减少请求用户的骚扰,一步到位.
2.简化代码,让代码出错率降低.
因此我们可以建这样一个类:
.h 文件
@interface JDAuthorityManager : NSObject
/**请求 APP 需要的权限*/
+ (void)requestAuthority;@end
.m 文件
#import "JDAuthorityManager.h"
#import <AVFoundation/AVFoundation.h> //相机
#import <Photos/Photos.h> //相册
#import <CoreLocation/CoreLocation.h> //定位
#import <AddressBook/AddressBook.h> //通讯录static NSString *authorityStr =@"authority";@implementation JDAuthorityManager+ (void)requestAuthority{//只请求一次NSUserDefaults *defaut =[NSUserDefaults standardUserDefaults];NSString *authority = [defaut objectForKey:authorityStr];if (authority) {return;}[defaut setObject:@"authority" forKey:authorityStr];//相册权限PHAuthorizationStatus photoAuthorStatus = [PHPhotoLibrary authorizationStatus];if (photoAuthorStatus == PHAuthorizationStatusNotDetermined){[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {}];}//相机AVAuthorizationStatus avstatus =[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];if (avstatus == AVAuthorizationStatusNotDetermined) {[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {}];}//麦克风AVAuthorizationStatus micstatus =[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];if (micstatus == AVAuthorizationStatusNotDetermined) {[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {}];}//定位权限CLAuthorizationStatus cLstatus = [CLLocationManager authorizationStatus];if (cLstatus == kCLAuthorizationStatusNotDetermined) {[[[CLLocationManager alloc]init] requestWhenInUseAuthorization];[[[CLLocationManager alloc]init] requestAlwaysAuthorization];}//通讯录 ios9之前ABAuthorizationStatus abstatus =ABAddressBookGetAuthorizationStatus();if (abstatus == kABAuthorizationStatusNotDetermined) {ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {});}//日历或者备忘录
}@end
然后在 appdelegate 中调用一下.
假设我们事先已经请求过权限了,前面的方法就可以简化为
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];if (status == CNAuthorizationStatusAuthorized){[self openContact]}else{//无权限}
是不是麻烦的权限问题迎刃而解?
Demo地址:https://github.com/yuying2012/WJDStudyLibrary
这是一个大工程,请从工程中寻找相关模块代码.