携手创造,共同成长!这是我参加「日新计划 8 月更文应战」的第35天,点击查看活动概况

前言

维护用户的隐私不被侵害是每个开发者应该承当的职责

用户隐私现已不断被资产化,经过商业运作成为了一种牟利的手段。apple在用户隐私维护方面一直走在前列.遵循运用最少的权限来实现功能 例如给全部权限的话,有些app就偷偷读相册,根据相册给推产品

在iOS14相册 iOS 14 相册权限增加了 Limited Photo 形式 ,新增挑选权限类型 PHAuthorizationStatusLimited

    PHAuthorizationStatusLimited API_AVAILABLE(ios(14)), // User has authorized this application for limited photo library access. Add PHPhotoLibraryPreventAutomaticLimitedAccessAlert = YES to the application's Info.plist to prevent the automatic alert to update the users limited library selection. Use -[PHPhotoLibrary(PhotosUISupport) presentLimitedLibraryPickerFromViewController:] from PhotosUI/PHPhotoLibrary+PhotosUISupport.h to manually present the limited library picker.

iOS小技能:iOS14相册权限适配 (Limited Photo Library Access)

I iOS14相册权限适配

预备常识: 冷启动: App 不在内存中/没有相关的进程存在 热启动:(从后台康复)内存有部分,无相关的进程存在 暂停:存在内存中,存在相关进程

1.1 挑选答应被拜访的图片资源

用户在冷启 APP 运用 PhotoKit 拜访资源的时分会默许弹出体系图片挑选界面,让用户挑选答应被拜访的资源:

当页面弹出恳求权限 Alert 时,会有挑选相片...选项,用户挑选该选项时,会弹出页面供用户挑选答应App拜访的相片。

iOS小技能:iOS14相册权限适配 (Limited Photo Library Access)
后续有两种方式来修正用户挑选的资源:

  1. 手动触发挑选/撤销挑选图片以移除拜访权限的界面 : [[PHPhotoLibrary sharedPhotoLibrary] presentLimitedLibraryPickerFromViewController:self];

iOS小技能:iOS14相册权限适配 (Limited Photo Library Access)

  1. 在运用设置->Photos->Edit Selected Photos中修正

部分相册权限时,界面供给直接跳设置的进口

iOS小技能:iOS14相册权限适配 (Limited Photo Library Access)

iOS小技能:iOS14相册权限适配 (Limited Photo Library Access)

1.2 相册权限API的相关改动

  • 新增了资源恳求和获取的 API,并且将老的资源恳求 API 标为抛弃
/// Replaces \c +authorizationStatus to support add-only/read-write access level status
+ (PHAuthorizationStatus)authorizationStatusForAccessLevel:(PHAccessLevel)accessLevel API_AVAILABLE(macosx(11.0), ios(14), tvos(14));
+ (void)requestAuthorizationForAccessLevel:(PHAccessLevel)accessLevel handler:(void(^)(PHAuthorizationStatus status))handler API_AVAILABLE(macosx(11.0), ios(14), tvos(14)) NS_SWIFT_ASYNC(2);
  • Limited Photo 形式:PHAuthorizationStatus 增加新的枚举 PHAuthorizationStatusLimited
typedef NS_ENUM(NSInteger, PHAuthorizationStatus) {
    PHAuthorizationStatusNotDetermined = 0, // User has not yet made a choice with regards to this application
    PHAuthorizationStatusRestricted,        // This application is not authorized to access photo data.
                                            // The user cannot change this application’s status, possibly due to active restrictions
                                            //   such as parental controls being in place.
    PHAuthorizationStatusDenied,            // User has explicitly denied this application access to photos data.
    PHAuthorizationStatusAuthorized,        // User has authorized this application to access photos data.
    PHAuthorizationStatusLimited API_AVAILABLE(ios(14)), // User has authorized this application for limited photo library access. Add PHPhotoLibraryPreventAutomaticLimitedAccessAlert = YES to the application's Info.plist to prevent the automatic alert to update the users limited library selection. Use -[PHPhotoLibrary(PhotosUISupport) presentLimitedLibraryPickerFromViewController:] from PhotosUI/PHPhotoLibrary+PhotosUISupport.h to manually present the limited library picker.
};
  • 相册拜访方式: PHAccessLevel (恳求查询limited权限在 accessLevel 为 readAndWrite 时收效)
typedef NS_ENUM(NSInteger, PHAccessLevel) {
    PHAccessLevelAddOnly = 1,
    PHAccessLevelReadWrite = 2,
} API_AVAILABLE(macos(11.0), ios(14), tvos(14));

iOS小技能:iOS14相册权限适配 (Limited Photo Library Access)

1.3 适配demo

从CSDN下载Demo源码:https://download.csdn.net/download/u011018979/18543032

关键:APP 需要监听用户修正了答应拜访的资源并且更新资源列表

private

  1. 手动触发挑选更多相片或撤销挑选以移除拜访权限的界面: [[PHPhotoLibrary sharedPhotoLibrary] presentLimitedLibraryPickerFromViewController:self];
  2. 显现答应拜访的相册:+ (PHFetchResult<PHAssetCollection *> *)fetchAssetCollectionsWithType:(PHAssetCollectionType)type subtype:(PHAssetCollectionSubtype)subtype options:(PHFetchOptions *)options;
  3. 恳求limited 权限:requestAuthorizationForAccessLevel:handler:
  4. 查询权限相册授权状况:+ (PHAuthorizationStatus)authorizationStatusForAccessLevel:(PHAccessLevel)accessLevel API_AVAILABLE(macosx(11.0), ios(14), tvos(14));
  5. 图片挑选器:单选UIImagePickerController
  6. 图片挑选器: 多选PHPickerViewController

II 具体适配方案

2.1 手动触发挑选/撤销挑选图片以移除拜访权限的界面

优化挑选图片弹窗提示的交互方式:官方主张关闭自动弹窗,推荐在用户挑选了 Limited Photo 形式时,在特定界面(例如设置页)显现修正资源的进口,当用户自动点击该进口时弹出体系弹窗。

  1. 关闭体系自动的挑选弹窗提示:能够在 info.plist 中设置 PHPhotoLibraryPreventAutomaticLimitedAccessAlert 选项为 YES ,
	<key>NSPhotoLibraryUsageDescription</key>
	<string>你能够共享相机胶卷中的相片、将相片保存</string>
	<key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
	<false/>
  1. 手动触发挑选更多相片或撤销挑选以移除拜访权限的界面:
[[PHPhotoLibrary sharedPhotoLibrary] presentLimitedLibraryPickerFromViewController:self];

iOS小技能:iOS14相册权限适配 (Limited Photo Library Access)

2.2 相册拜访方式

还在运用 AssetsLibrary 管理资源读写的赶快迁移到 PhotoKit,关于只读,主张运用PHPicker。

3.2.1 只读权限

只读: 主张运用 iOS 14 供给的图片挑选器 PHPicker 来挑选图片资源(iOS 14 以下对应 UIImagePickerController)

PHPicker 长处:独立进程,不影响 APP 功能。不需要用户颁发权限就能够挑选用户所有的资源, 支撑多选。

对应的署理协议如下

@interface ViewController () <UINavigationControllerDelegate, UIImagePickerControllerDelegate, PHPickerViewControllerDelegate>

具体的实现代码请看第三章节。

3.2.2 只写权限

需要在 Info.plist 下配置权限信息

    <key>NSPhotoLibraryAddUsageDescription</key>
    <string>你能够共享相机胶卷中的相片、将相片保存</string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>你能够共享相机胶卷中的相片</string>
    <key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
    <false/>

别离运用 AssetsLibraryPhotoKit 以及 UIKit 层 UIImageWriteToSavedPhotosAlbum 写入相册

- (IBAction)saveClick:(UIButton *)sender {
    //参数1:图片对象
    //参数2:成功办法绑定的target
    //参数3:成功后调用办法
    //参数4:需要传递信息(成功后调用办法的参数) 一般写nil
    UIImageWriteToSavedPhotosAlbum(self.imageView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
    /* 1.先保存图片到【相机胶卷】(不能直接保存到自定义相册中)
        1> C言语函数
        2> AssetsLibrary框架  (iOS4支撑,iOS9.0被抛弃)
        3> Photos框架 (iOS 8.0支撑 推荐运用)
       2.具有一个【自定义相册】
        1> AssetsLibrary框架
        2> Photos框架
       3.将方才保存到【相机胶卷】里面的图片引用到【自定义相册】
        1> AssetsLibrary框架
        2> Photos框架
     */
}
#pragma mark -- <保存到相册>
-(void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    NSString *msg = nil ;
    if(error){
        msg = @"保存图片失败" ;
    }else{
        msg = @"保存图片成功" ;
    }
}

iOS小技能:iOS14相册权限适配 (Limited Photo Library Access)

3.2.3 读写权限

别离运用 AssetsLibrary 和 PhotoKit 来读取相册资源

2.3 其他需要注意的API

运用 PHAssetCreationRequests 来创建的资源默许是会增加在用户答应的集合傍边

2.4 监听第一次相册授权时

  1. 监听到用户点击不答应,不答应时显现引导。
  2. 用户未作出清晰挑选的状况下自己自动恳求了一次权限设置(改写UI的代码放到主线程履行)

更多内容请看此篇文章:blog.csdn.net/z929118967/…

/**
 1. 监听到用户点击不答应,不答应时显现引导
 2. 用户未作出清晰挑选的状况下自己自动恳求了一次权限设置
 showAlert:不答应时显现引导
 block: 答应之后的动作,比如保存图片
 */
+(BOOL)isHasPhotoLibraryAuthorityWithisShowAlert:(BOOL)showAlert  block:(void (^)(id sender))block
{
    PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus] ;
//1.  定义部分block:  处理没有权限的状况,显现引导
    BOOL (^block4none)(PHAuthorizationStatus ) = ^ BOOL (PHAuthorizationStatus status ){
        NSLog(@" 没有拜访图库的权限==============");
        if (showAlert) {
            [LBAlertController showAlertTitle:@"无法运用相册" content:@"请在iPhone的\"设置-隐私-相片\"中答应拜访相片。" cancelString:@"撤销" cancleBlock:nil sureString:@"去设置" sureBlock:^{
                // 需要在info.plist中增加 URL types 并设置一项URL Schemes为prefs  IOS10 以后不起作用
                    if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]){
                    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
                }
            } currentController:[QCT_Common getCurrentVC]];
        }
        return NO;
    };
    switch (status) {
        case PHAuthorizationStatusRestricted:
        case PHAuthorizationStatusDenied : {
//没有拜访图库的权限
            return block4none(status);
        }
            break;
        case PHAuthorizationStatusNotDetermined:{//2. 用户未作出清晰挑选的状况下自己自动恳求了一次权限设置
            [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus phStatus) {
                //监听到用户点击不答应,不答应时显现引导
                if (phStatus == PHAuthorizationStatusRestricted || phStatus == PHAuthorizationStatusDenied) {
                    dispatch_sync(dispatch_get_main_queue(), ^{
                        //改写UI的代码放到主线程履行
                        block4none(status);
                    });
                } else if (phStatus == PHAuthorizationStatusNotDetermined) {
                    // 不处理
                } else {
                    // 履行外围的block
//                    status = QMUIAssetAuthorizationStatusAuthorized;
                    if(block){//履行答应之后的保存图片操作
                        block(nil);
                    }
                }
            }];
            return  NO;
        }
        default:
            break;
    }
    if(block){// 3. 履行答应之后的保存图片操作
        block(nil);
    }
    return  YES;
}

III 挑选图片资源视图

运用PHPicker和UIImagePickerController挑选图片资源

3.1 恳求查询权限

/**
 恳求查询权限
 */
- (IBAction)requestAuth:(id)sender
{
    // 恳求权限,需注意 limited 权限尽在 accessLevel 为 readAndWrite 时收效
    PHAccessLevel level1 = PHAccessLevelAddOnly;// 仅答应增加相片
    PHAccessLevel level2 = PHAccessLevelReadWrite;// 答应拜访相片,limitedLevel 必须为 readWrite
    [PHPhotoLibrary requestAuthorizationForAccessLevel:level2 handler:^(PHAuthorizationStatus status) {
      switch (status) {
      /**
        恳求查询限制权限:需注意 `PHAuthorizationStatusLimited` 权限在 accessLevel 为 `PHAccessLevelReadWrite` 时收效
      */
          case PHAuthorizationStatusLimited:
              NSLog(@"limited");
              break;
          case PHAuthorizationStatusDenied:
              NSLog(@"denied");
              break;
          case PHAuthorizationStatusAuthorized:
              NSLog(@"authorized");
              break;
          default:
              break;
      }
    }];
}

3.2 运用UIImagePickerController挑选图片资源(单选)

  • 初始化
#pragma mark - 单选
/**
 UIImagePickerController
 */
- (UIImagePickerController *)picker
{
    if (!_picker) {
        _picker = [[UIImagePickerController alloc]init];
    }
    return _picker;
}
- (IBAction)openPickerAciton:(id)sender
{
    self.isDoing = NO;
    if (self.isDoing) {
        return;
    }
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary] == NO) {
        return;
    }
    self.picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    self.isDoing = YES;
    [self presentViewController:self.picker animated:YES completion:nil];
}
  • 处理署理

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
    UIImage *image = info[UIImagePickerControllerOriginalImage];
    self.imageView.image = image;
    [picker dismissViewControllerAnimated:YES completion:nil];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    [picker dismissViewControllerAnimated:YES completion:nil];
}

3.3 运用PHPicker挑选图片资源(多选)

  • 初始化
#pragma mark - ******** 多选
- (IBAction)openNewPicker:(id)sender
{
    //三种过滤类型
    PHPickerFilter *imagesFilter = PHPickerFilter.imagesFilter;
    PHPickerFilter *videosFilter = PHPickerFilter.videosFilter;
    PHPickerFilter *livePhotosFilter = PHPickerFilter.livePhotosFilter;
    PHPickerConfiguration *configuration = [[PHPickerConfiguration alloc] init];
    configuration.filter = [PHPickerFilter anyFilterMatchingSubfilters:@[imagesFilter]]; // 可配置查询用户相册中文件的类型,支撑三种
    configuration.selectionLimit = 0; // 默许为1,为0为跟从体系上限
    PHPickerViewController *picker = [[PHPickerViewController alloc] initWithConfiguration:configuration];
    picker.delegate = self;
    [self presentViewController:picker animated:YES completion:nil];
}
  • 处理署理

- (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray<PHPickerResult *> *)results API_AVAILABLE(ios(14)) {
    [picker dismissViewControllerAnimated:YES completion:nil];
     if (!results || !results.count) {
         return;
     }
     NSItemProvider *itemProvider = results.firstObject.itemProvider;
     if ([itemProvider canLoadObjectOfClass:UIImage.class]) {
         __weak typeof(self) weakSelf = self;
         //异步获取
         [itemProvider loadObjectOfClass:UIImage.class completionHandler:^(__kindof id<NSItemProviderReading>  _Nullable object, NSError * _Nullable error) {
             if ([object isKindOfClass:UIImage.class]) {
                 __strong typeof(self) strongSelf = weakSelf;
                 dispatch_async(dispatch_get_main_queue(), ^{
                     strongSelf.imageView.image = (UIImage *)object;
                 });
             }
         }];
     }
}

see also

公号:iOS逆向