最近需要适配iOS 14的相册权限,在网上查了很多资料,大部分资料都只讲一部分缺另一部分,想把整个相册权限适配好还是有点麻烦,现在办把我整个适配过程记录下来,需要适配的兄弟可以参考一下
在 iOS13 及以前,当用户首次访问应用程序时,会被要求开放大量权限,比如相册、定位、联系人,实际上该应用可能仅仅需要一个选择图片功能,却被要求开放整个照片库的权限,这确实是不合理的。对于相册,在 iOS14 中引入了 “LimitedPhotos Library” 的概念,用户可以授予应用访问其一部分的照片,对于应用来说,仅能读取到用户选择让应用来读取的照片,让我们看到了 Apple 对于用户隐私的尊重。这仅仅是一部分,在iOS14 中,可以看到诸多类似的保护用户隐私的措施,也需要我们升级适配。
iOS14 新增了“Limited Photo Library Access” 模式,在授权弹窗中增加了 Select Photo 选项。用户可以在 App 请求调用相册时选择部分照片让 App 读取。从 App 的视⻆来看,你的相册里就只有这几张照片,App 无法得知其它照片的存在。
当一下次在进入时有会弹提示框,让你选择更多图片或者保留当前选择,这个提示框不太友好,可以在plist文件里面通过设置 Prevent limited photos access alert = YES隐藏提示
当选择添加更多图片是,可以通过 [[PHPhotoLibrary sharedPhotoLibrary] presentLimitedLibraryPickerFromViewController:self];到相册添加删除图片,然后监听图片变化做相应处理。
对于PHAuthorizationStatusLimited权限的适配,我采用的方案是,先绘制一个列表用于显示授权的图片,图片的资源也就是授权访问的,当要添加更多图片是通过 [[PHPhotoLibrary sharedPhotoLibrary] presentLimitedLibraryPickerFromViewController:self];跳转到相册,添加图片,添加完成后监听图片变化,更新列表,下面是我的代码:
if (@available(iOS 14.0, *)) {
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatusForAccessLevel:PHAccessLevelReadWrite];
switch(status) {
case PHAuthorizationStatusNotDetermined:
[self dispatchAuthorizationForAccessLevel];
break;
case PHAuthorizationStatusLimited:
NSLog(@"limited");
[selfe_enterGDPickerVC];
break;
// 用户拒绝当前应用访问相册
case PHAuthorizationStatusDenied:
NSLog(@"denied");
[self disptachPHAuthorizationStatusDenied];
break;
// 用户允许当前应用访问相册
case PHAuthorizationStatusAuthorized:
NSLog(@"authorized");
[self P_enterPHPickerViewController];
break;
default:
break;
}
}
/用户第一次访问
-(void)dispatchAuthorizationForAccessLevel{
[PHPhotoLibrary requestAuthorizationForAccessLevel:PHAccessLevelReadWrite handler:^(PHAuthorizationStatus status) {
switch(status) {
case PHAuthorizationStatusLimited:
{
dispatch_async(dispatch_get_main_queue(), ^{
[selfe_enterGDPickerVC];
});
break;
}
case PHAuthorizationStatusDenied:
{
NSLog(@"denied");
}
break;
case PHAuthorizationStatusAuthorized:
{
}
break;
default:
break;
}
}];
}
//进入授权访问的图片控制器
-(void)e_enterGDPickerVC{
MJWeakSelf;
GDPickerVC *picker = [GDPickerVC new];
picker.currentImageContent= ^(UIImage*_NonnullimageContent) {
NSLog(@"--选择图片--");
if(!imageContent) {
return;
}
[weakSelfanalysisImageWithContent:imageContent];
};
UINavigationController *nv = [[UINavigationController alloc] initWithRootViewController:picker];
nv.modalPresentationStyle =0;
[_controller presentViewController:nv animated:YES completion:nil];
}
----------授权图片列表--------------
//
//
#import "GDPickerVC.h"
#import
#import "GDPickerCollectionViewCell.h"
#define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)
// 屏幕高度,会根据横竖屏的变化而变化
#define SCREEN_HEIGHT ([[UIScreen mainScreen] bounds].size.height)
@interface GDPickerVC ()
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) NSMutableArray *photoArray;
@end
@implementation GDPickerVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.title = @"显示允许访问的图片";
//左边返回按钮
UIButton *fanHuiButton = [UIButton buttonWithType:UIButtonTypeCustom];
fanHuiButton.frame=CGRectMake(0,0,30,40);
[fanHuiButtonsetTitle:@"取消"forState:0];
[fanHuiButtonsetTitleColor:[UIColor systemBlueColor] forState:0];
[fanHuiButtonaddTarget:self action:@selector(comebackFuncation) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *leftItem = [[UIBarButtonItem alloc] initWithCustomView:fanHuiButton];
self.navigationItem.leftBarButtonItem = leftItem;
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeCustom];
rightButton.frame=CGRectMake(0,0,30,40);
[rightButtonsetTitle:@"添加"forState:0];
[rightButtonsetTitleColor:[UIColor systemBlueColor] forState:0];
[rightButtonaddTarget:self action:@selector(confirmButton:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *rightItem = [[UIBarButtonItem alloc] initWithCustomView:rightButton];
self.navigationItem.rightBarButtonItem = rightItem;
//获取图片
[self getLibarayPhotoImage];
self.photoArray = [NSMutableArray array];
[self.view addSubview:self.collectionView];
}
#pragma mark - UICollectionViewDataSource
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(SCREEN_WIDTH/3-2,SCREEN_WIDTH/3);
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.photoArray.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UIImage*image =self.photoArray[indexPath.row];
GDPickerCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass(GDPickerCollectionViewCell.class) forIndexPath:indexPath];
cell.imgView.image= image;
returncell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
UIImage*image =self.photoArray[indexPath.row];
if (_currentImageContent&&image) {
_currentImageContent(image);
[self comebackFuncation];
}
}
#pragma response
//返回
-(void)comebackFuncation{
[self dismissViewControllerAnimated:YES completion:nil];
}
//点击确认回调数据
-(void)confirmButton:(UIButton *)btn{
[[PHPhotoLibrary sharedPhotoLibrary] presentLimitedLibraryPickerFromViewController:self];
[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
}
- (void)photoLibraryDidChange:(PHChange *)changeInstance
{
[self.photoArray removeAllObjects];
[self getLibarayPhotoImage];
}
/* 遍历相簿中的全部图片
* @param assetCollection 相簿
* @param original 是否要原图
*/
-(void)getLibarayPhotoImage01{
__weaktypeof(self) weakSelf =self;
NSMutableArray
//获取可访问的图片配置选项
PHFetchOptions *option = [[PHFetchOptions alloc] init];
//根据图片的创建时间升序排序返回
option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
//获取类型为image的资源
PHFetchResult *result = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:option];
//遍历出每个PHAsset资源对象
[resultenumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
PHAsset*asset = (PHAsset*)obj;
//将PHAsset解析为image的配置选项
PHImageRequestOptions *requestOptions = [[PHImageRequestOptions alloc] init];
//图像缩放模式
requestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
//图片质量
requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
//PHImageManager解析图片
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:requestOptions resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
NSLog(@"图片 %@",result);
//在这里可以自定义一个显示可访问相册资源的viewController.
if(result) {
[weakSelf.photoArrayaddObject:result];
}
}];
dispatch_async(dispatch_get_main_queue(), ^{
[self.collectionView reloadData];
});
}];
}
-(void)getLibarayPhotoImage{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
PHFetchResult
for(PHAssetCollection*assetCollectioninassetCollections) {
[self enumerateAssetsInAssetCollection:assetCollection original:YES];
}
PHAssetCollection *cameraRoll = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary options:nil].lastObject;
[self enumerateAssetsInAssetCollection:cameraRoll original:YES];
});
}
- (void)enumerateAssetsInAssetCollection:(PHAssetCollection *)assetCollectionoriginal:(BOOL)original
{
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.resizeMode = PHImageRequestOptionsResizeModeFast;
options.synchronous=YES;
PHFetchResult
for(PHAsset*assetinassets) {
CGSize size = original ? CGSizeMake(asset.pixelWidth, asset.pixelHeight) : CGSizeZero;
__weaktypeof(self) weakSelf =self;
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:size contentMode:PHImageContentModeDefault options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
NSLog(@"%@", result);
if(result) {
original ? [weakSelf.photoArrayaddObject:result] : [weakSelf.photoArrayaddObject:result];
}
}];
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.collectionViewreloadData];
});
}
}
#pragma mark--懒加载
- (UICollectionView *)collectionView{
if (!_collectionView) {
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
flowLayout.minimumLineSpacing=2;
flowLayout.minimumInteritemSpacing = 1;
UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:flowLayout];
collectionView.backgroundColor = self.view.backgroundColor;
collectionView.delegate=self;
collectionView.dataSource=self;
[collectionViewregisterClass:GDPickerCollectionViewCell.class forCellWithReuseIdentifier:NSStringFromClass(GDPickerCollectionViewCell.class)];
_collectionView= collectionView;
}
return _collectionView;
}
@end
上面是PHAuthorizationStatusLimited 用户已授权此应用程序进行有限照片库访问(iOS14新增)的适配,下面在来说一下适配允许访问所有图片,下面是适配代码
//ios14后使用
-(void)P_enterPHPickerViewController{
MJWeakSelf;
//用户选择"允许访问所有照片",调用PHPickerViewController显示图片选择器
dispatch_async(dispatch_get_main_queue(), ^{
PHPickerConfiguration *configuration = [[PHPickerConfiguration alloc] init];
//只获取image类型资源
configuration.filter = [PHPickerFilter imagesFilter];
//可以多选
// configuration.selectionLimit = 1;
PHPickerViewController *pickerVC = [[PHPickerViewController alloc] initWithConfiguration:configuration];
pickerVC.delegate=self;
pickerVC.modalPresentationStyle = UIModalPresentationFullScreen;
[weakSelf.controller presentViewController:pickerVC animated:YES completion:^{
}];
});
}
- (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray
{
MJWeakSelf;
[pickerdismissViewControllerAnimated:YES completion:nil];
if(!results || !results.count) {
return;
}
NSLog(@"didFinishPicking");
NSItemProvider *itemProvider = results.firstObject.itemProvider;
if ([itemProvider canLoadObjectOfClass:UIImage.class]) {
__weaktypeof(self) weakSelf =self;
[itemProviderloadObjectOfClass:UIImage.class completionHandler:^(__kindof id
if([objectisKindOfClass:UIImage.class]) {
UIImage*image = (UIImage*)object;
[weakSelfanalysisImageWithContent:image];
}
}];
}
}