前言
iOS开发避免不开系统权限的问题,如何在APP中以更加友好的方式向用户展示系统权限,似乎也是开发过程中指的深思的一件事。
那如何提高用户获取权限的通过率呢?以下几种方式或许是不错的尝试:
1.在用户打开app时就向用户请求权限。
- 告知用户授权权限后能够获得的好处后再向用户请求权限。
3.在绝对必要的情况下才向用户请求权限。
4.在展示系统权限的提示框前,现象用户显示自定义的提示框,如果用户不允许,默认无操作,如果用户允许,再展示系统提示框。
上面的只是一些尝试,与本文的主要讲述内容关系不大,接下来我们主要来看一下常用的一些系统权限的状态获取以及主动唤起权限请求的方法。
一、常用的权限及转换枚举
1 常用的权限
iOS系统的权限包括很多,本文主要整理了笔者常用的一些权限,后面随着使用可能会继续补充,目前本文中主要包含的权限如下:
相机权限
相册权限
日历权限
麦克风权限
推送权限
定位权限
提醒事项权限
通讯录权限
互联网权限
蓝牙权限
2 自定义权限状态枚举
typedef NS_ENUM(NSInteger, TenAuthorizationStatus) {
/**未确定*/
TenAuthorizationStatusNotDetermined = 0,
/**限制*/
TenAuthorizationStatusRestricted,
/**拒绝*/
TenAuthorizationStatusDenied,
/**同意授权*/
TenAuthorizationStatusAuthorized,
};
二、权限的获取及请求
1 相机权限
引入头文件#import <AVFoundation/AVFoundation.h>
1.1 权限获取
+(TenAuthorizationStatus)cameraStatue {
TenAuthorizationStatus status;
switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) {
case AVAuthorizationStatusAuthorized:
status = TenAuthorizationStatusAuthorized;
break;
case AVAuthorizationStatusNotDetermined:
status = TenAuthorizationStatusNotDetermined;
break;
case AVAuthorizationStatusRestricted:
status = TenAuthorizationStatusRestricted;
break;
default:
status = TenAuthorizationStatusDenied;
break;
}
return status;
}
1.2 权限请求
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
}];
1.3 info.plist设置
Privacy - Camera Usage Description
2 相册权限
引入头文件#import <Photos/Photos.h>
2.1 权限获取
+(TenAuthorizationStatus)photoLibraryStatus {
TenAuthorizationStatus status;
EHIAuthorizationStatus status;
if (@available(iOS 14, *)) {
switch ([PHPhotoLibrary authorizationStatusForAccessLevel:PHAccessLevelReadWrite]) {
case PHAuthorizationStatusAuthorized:
//全部
status = TenAuthorizationStatusAuthorized;
break;
case PHAuthorizationStatusLimited:
//部分
status = TenAuthorizationStatusAuthorized;
break;
case PHAuthorizationStatusNotDetermined:
status = TenAuthorizationStatusNotDetermined;
break;
case PHAuthorizationStatusRestricted:
status = TenAuthorizationStatusRestricted;
break;
default:
status = TenAuthorizationStatusDenied;
break;
}
}else {
switch ([PHPhotoLibrary authorizationStatus]) {
case PHAuthorizationStatusAuthorized:
status = TenAuthorizationStatusAuthorized;
break;
case PHAuthorizationStatusNotDetermined:
status = TenAuthorizationStatusNotDetermined;
break;
case PHAuthorizationStatusRestricted:
status = TenAuthorizationStatusRestricted;
break;
default:
status = TenAuthorizationStatusDenied;
break;
}
}
return status;
}
2.2 权限请求
if (@available(iOS 14, *)) {
[PHPhotoLibrary requestAuthorizationForAccessLevel:PHAccessLevelReadWrite handler:^(PHAuthorizationStatus status) {
}];
} else {
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
}];
}
2.3 info.plist设置
存储:
Privacy - Photo Library Additions Usage Description
查看:Privacy - Photo Library Usage Description
3 日历权限
引入头文件#import <EventKit/EventKit.h>
3.1 权限获取
+(TenAuthorizationStatus)calendarStatus {
TenAuthorizationStatus status;
switch ([EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent]) {
case EKAuthorizationStatusAuthorized:
status = TenAuthorizationStatusAuthorized;
break;
case EKAuthorizationStatusNotDetermined:
status = TenAuthorizationStatusNotDetermined;
break;
case EKAuthorizationStatusRestricted:
status = TenAuthorizationStatusRestricted;
break;
default:
status = TenAuthorizationStatusDenied;
break;
}
return status;
}
3.2 权限请求
[[[EKEventStore alloc] init] requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError * _Nullable error) {
}];
3.3 info.plist设置
Privacy - Calendars Usage Description
4 麦克风权限
引入头文件#import <AVFoundation/AVFoundation.h>
4.1 权限获取
+(TenAuthorizationStatus)microphoneStatus {
TenAuthorizationStatus status;
switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]) {
case AVAuthorizationStatusAuthorized:
status = TenAuthorizationStatusAuthorized;
break;
case AVAuthorizationStatusNotDetermined:
status = TenAuthorizationStatusNotDetermined;
break;
case AVAuthorizationStatusRestricted:
status = TenAuthorizationStatusRestricted;
break;
default:
status = TenAuthorizationStatusDenied;
break;
}
return status;
}
4.2 权限请求
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
}];
4.3 info.plist设置
Privacy - Microphone Usage Description
5 推送权限
引入头文件#import <UserNotifications/UserNotifications.h>
5.1 权限获取
+(TenAuthorizationStatus)pushStatus {
if (@available(iOS 10.0, *)) {
dispatch_semaphore_t singal = dispatch_semaphore_create(0);//创建信号量
__block TenAuthorizationStatus status;
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
switch (settings.authorizationStatus) {
case UNAuthorizationStatusDenied:
status = TenAuthorizationStatusDenied;
break;
case UNAuthorizationStatusNotDetermined:
status = TenAuthorizationStatusNotDetermined;
break;
default:
status = TenAuthorizationStatusAuthorized;
break;
}
dispatch_semaphore_signal(singal);//赋值完成发送信号
}];
dispatch_semaphore_wait(singal, DISPATCH_TIME_FOREVER);//等待信号
return status;
}
return ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone) ? TenAuthorizationStatusDenied : TenAuthorizationStatusAuthorized;
}
5.2 权限请求
if (@available(iOS 10.0, *)) {
[[UNUserNotificationCenter currentNotificationCenter]requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {
}];
}else {
//iOS10之前,系统对于申请推送权限没有具体的API,只有设置NotificationSettings时,会自动请求权限
UIUserNotificationSettings *setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:setting];
}
6 定位权限
引入头文件#import <CoreLocation/CoreLocation.h>
6.1 权限获取
+(TenAuthorizationStatus)locationStatus {
if (![CLLocationManager locationServicesEnabled]) {
//手机定位权限未开启(针对全部应用)
return TenAuthorizationStatusRestricted;
}
TenAuthorizationStatus status;
switch ([EHILocationManager shareManager].authorizationStatus) {
case kCLAuthorizationStatusNotDetermined:
status = TenAuthorizationStatusNotDetermined;
break;
case kCLAuthorizationStatusDenied:
status = TenAuthorizationStatusDenied;
break;
case kCLAuthorizationStatusRestricted:
status = TenAuthorizationStatusRestricted;
break;
default:
status = TenAuthorizationStatusAuthorized;
break;
}
return status;
}
6.2 权限请求
定位权限请求与其他权限不动,请求的结果是在代理中返回的,可以自己做一层封装,与其他系统权限请求一样用block的形式将结果返回。
封装方法调用
[[TenLocationManager shareManager] ten_requestLocationRequest:^(BOOL granted, CLAuthorizationStatus status) {
}];
TenLocationManager.h
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
@interface TenLocationManager : NSObject
/**系统定位权限*/
@property (nonatomic, readonly) CLAuthorizationStatus authorizationStatus;
/**单例方法*/
+(instancetype)shareManager;
/**
唤起请求定位权限
@param block 结果回调
*/
-(void)ten_requestLocationRequest:(void (^) (BOOL granted, CLAuthorizationStatus status))block;
@end
TenLocationManager.m
#import "TenLocationManager.h"
@interface TenLocationManager () <CLLocationManagerDelegate>
/**请求定位权限结果类*/
@property (nonatomic, copy) void (^locationAuthBlock) (BOOL granted, CLAuthorizationStatus state);
/**系统定位*/
@property (nonatomic, strong) CLLocationManager *sys_locationManager;
@end
static TenLocationManager *_locationManager;
@implementation TenLocationManager
/**构造方法*/
+(instancetype)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (_locationManager == nil) {
_locationManager = [super allocWithZone:zone];
}
});
return _locationManager;
}
/**单例方法*/
+(instancetype)shareManager {
return [[self alloc] init];
}
/**唤起定位权限请求*/
-(void)ten_requestLocationRequest:(void (^)(BOOL, CLAuthorizationStatus))block {
self.locationAuthBlock = block;
[self.sys_locationManager requestWhenInUseAuthorization];
}
#pragma mark - CLLocationManagerDelegate
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (self.locationAuthBlock) {
self.locationAuthBlock(status == kCLAuthorizationStatusAuthorizedWhenInUse, status);
}
}
#pragma mark - lazy getter
-(CLLocationManager *)sys_locationManager {
if (!_sys_locationManager) {
_sys_locationManager = [[CLLocationManager alloc]init];
_sys_locationManager.delegate = self;
}
return _sys_locationManager;
}
- (CLAuthorizationStatus)authorizationStatus {
if (@available(iOS 14, *)) {
return self.sys_locationManager.authorizationStatus;
}else {
return [CLLocationManager authorizationStatus];
}
}
6.3 info.plist设置
总是:
Privacy - Location Always Usage Description
使用中:Privacy - Location When In Use Usage Description
iOS14 之后,增加精确定位功能,通过设置NSLocationDefaultAccuracyReduced为true来实现默认请求大概位置,否则是默认请求精确位置
如果是大概位置,可单次请求精确位置,配置NSLocationTemporaryUsageDescriptionDictionary字段设置请求原因,
唤起精确定位方法:[self.mgr requestTemporaryFullAccuracyAuthorizationWithPurposeKey:@"purposeKey"];
7 提醒事项权限
引入头文件#import <EventKit/EventKit.h>
7.1 权限获取
+(TenAuthorizationStatus)remindStatus {
TenAuthorizationStatus status;
switch ([EKEventStore authorizationStatusForEntityType:EKEntityTypeReminder]) {
case EKAuthorizationStatusAuthorized:
status = TenAuthorizationStatusAuthorized;
break;
case EKAuthorizationStatusNotDetermined:
status = TenAuthorizationStatusNotDetermined;
break;
case EKAuthorizationStatusRestricted:
status = TenAuthorizationStatusRestricted;
break;
default:
status = TenAuthorizationStatusDenied;
break;
}
return status;
}
7.2 权限请求
[[[EKEventStore alloc] init] requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError * _Nullable error) {
}];
7.3 info.plist设置
NSRemindersUsageDescription
8 通讯录权限
引入头文件
iOS 9.0前 #import <AddressBook/AddressBook.h>
iOS 9.0后 #import <Contacts/Contacts.h>
8.1 权限获取
+(TenAuthorizationStatus)addressBookStatus {
TenAuthorizationStatus status;
if (@available(iOS 9.0, *)) {
switch ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts]) {
case CNAuthorizationStatusAuthorized:
status = TenAuthorizationStatusAuthorized;
break;
case CNAuthorizationStatusNotDetermined:
status = TenAuthorizationStatusNotDetermined;
break;
case CNAuthorizationStatusRestricted:
status = TenAuthorizationStatusRestricted;
break;
default:
status = TenAuthorizationStatusDenied;
break;
}
} else {
switch (ABAddressBookGetAuthorizationStatus()) {
case kABAuthorizationStatusAuthorized:
status = TenAuthorizationStatusAuthorized;
break;
case kABAuthorizationStatusNotDetermined:
status = TenAuthorizationStatusNotDetermined;
break;
default:
status = TenAuthorizationStatusDenied;
break;
}
}
return status;
}
8.2 权限请求
if (@available(iOS 9.0, *)) {
[[[CNContactStore alloc] init] requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
}];
}else {
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
});
}
8.3 info.plist设置
Privacy - Contacts Usage Description
9 互联网权限
引入头文件#import <CoreTelephony/CTCellularData.h>
9.1 权限获取
+(TenAuthorizationStatus)netStatus {
TenAuthorizationStatus status;
switch ([[CTCellularData alloc] init].restrictedState) {
case kCTCellularDataNotRestricted:
status = TenAuthorizationStatusAuthorized;
break;
case kCTCellularDataRestrictedStateUnknown:
status = TenAuthorizationStatusNotDetermined;
break;
default:
status = TenAuthorizationStatusDenied;
break;
}
return status;
}
9.2 权限请求
系统未提供接口供开发者手动请求网络权限,iOS10以上系统,应用首次请求网络会自动弹出,一个应用只弹出一次,卸载也不会重新弹出
10 蓝牙权限
蓝牙权限具体使用之前的表述有些问题,现在重新整理到了新的文章中:蓝牙权限的获取。