【iOS开发笔记】系统权限的获取和请求

前言

iOS开发避免不开系统权限的问题,如何在APP中以更加友好的方式向用户展示系统权限,似乎也是开发过程中指的深思的一件事。

那如何提高用户获取权限的通过率呢?以下几种方式或许是不错的尝试:

1.在用户打开app时就向用户请求权限。

  1. 告知用户授权权限后能够获得的好处后再向用户请求权限。

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 蓝牙权限

蓝牙权限具体使用之前的表述有些问题,现在重新整理到了新的文章中:蓝牙权限的获取

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349