? ? ?最近需要適配iOS 14的相冊(cè)權(quán)限,在網(wǎng)上查了很多資料伍纫,大部分資料都只講一部分缺另一部分,想把整個(gè)相冊(cè)權(quán)限適配好還是有點(diǎn)麻煩,現(xiàn)在辦把我整個(gè)適配過(guò)程記錄下來(lái)次哈,需要適配的兄弟可以參考一下
? ? ?在 iOS13 及以前,當(dāng)用戶首次訪問(wèn)應(yīng)用程序時(shí)吆录,會(huì)被要求開放大量權(quán)限窑滞,比如相冊(cè)、定位恢筝、聯(lián)系人哀卫,實(shí)際上該應(yīng)用可能僅僅需要一個(gè)選擇圖片功能,卻被要求開放整個(gè)照片庫(kù)的權(quán)限撬槽,這確實(shí)是不合理的此改。對(duì)于相冊(cè),在 iOS14 中引入了 “LimitedPhotos Library” 的概念侄柔,用戶可以授予應(yīng)用訪問(wèn)其一部分的照片带斑,對(duì)于應(yīng)用來(lái)說(shuō)鼓寺,僅能讀取到用戶選擇讓應(yīng)用來(lái)讀取的照片,讓我們看到了 Apple 對(duì)于用戶隱私的尊重勋磕。這僅僅是一部分妈候,在iOS14 中,可以看到諸多類似的保護(hù)用戶隱私的措施挂滓,也需要我們升級(jí)適配苦银。?
? ? ? iOS14 新增了“Limited Photo Library Access” 模式,在授權(quán)彈窗中增加了 Select Photo 選項(xiàng)赶站。用戶可以在 App 請(qǐng)求調(diào)用相冊(cè)時(shí)選擇部分照片讓 App 讀取幔虏。從 App 的視?來(lái)看,你的相冊(cè)里就只有這幾張照片贝椿,App 無(wú)法得知其它照片的存在想括。
? ?當(dāng)一下次在進(jìn)入時(shí)有會(huì)彈提示框,讓你選擇更多圖片或者保留當(dāng)前選擇烙博,這個(gè)提示框不太友好瑟蜈,可以在plist文件里面通過(guò)設(shè)置?Prevent limited photos access alert = YES隱藏提示
當(dāng)選擇添加更多圖片是,可以通過(guò)? ? [[PHPhotoLibrary sharedPhotoLibrary] presentLimitedLibraryPickerFromViewController:self];到相冊(cè)添加刪除圖片渣窜,然后監(jiān)聽圖片變化做相應(yīng)處理铺根。
對(duì)于PHAuthorizationStatusLimited權(quán)限的適配,我采用的方案是乔宿,先繪制一個(gè)列表用于顯示授權(quán)的圖片位迂,圖片的資源也就是授權(quán)訪問(wèn)的,當(dāng)要添加更多圖片是通過(guò)?[[PHPhotoLibrary sharedPhotoLibrary] presentLimitedLibraryPickerFromViewController:self];跳轉(zhuǎn)到相冊(cè)详瑞,添加圖片掂林,添加完成后監(jiān)聽圖片變化,更新列表坝橡,下面是我的代碼:
?if (@available(iOS 14.0, *)) {
? ? ? ? ? ? PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatusForAccessLevel:PHAccessLevelReadWrite];
? ? ? ? ? ? ? switch(status) {
? ? ? ? ? ? ? ? ? case PHAuthorizationStatusNotDetermined:
? ? ? ? ? ? ? ? ? ? ? [self dispatchAuthorizationForAccessLevel];
? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? case PHAuthorizationStatusLimited:
? ? ? ? ? ? ? ? ? ? ? NSLog(@"limited");
? ? ? ? ? ? ? ? ? ? ? [selfe_enterGDPickerVC];
? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? ? // 用戶拒絕當(dāng)前應(yīng)用訪問(wèn)相冊(cè)
? ? ? ? ? ? ? ? ? case PHAuthorizationStatusDenied:
? ? ? ? ? ? ? ? ? ? ? NSLog(@"denied");
? ? ? ? ? ? ? ? ? ? ? [self disptachPHAuthorizationStatusDenied];
? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? ? // 用戶允許當(dāng)前應(yīng)用訪問(wèn)相冊(cè)
? ? ? ? ? ? ? ? ? case PHAuthorizationStatusAuthorized:
? ? ? ? ? ? ? ? ? ? ? NSLog(@"authorized");
? ? ? ? ? ? ? ? ? ? ? [self P_enterPHPickerViewController];
? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? default:
? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? }
/用戶第一次訪問(wèn)
-(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;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }];
}
//進(jìn)入授權(quán)訪問(wèn)的圖片控制器
-(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];
}
----------授權(quán)圖片列表--------------
//
//
#import "GDPickerVC.h"
#import
#import "GDPickerCollectionViewCell.h"
#define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)
// 屏幕高度泻帮,會(huì)根據(jù)橫豎屏的變化而變化
#define SCREEN_HEIGHT ([[UIScreen mainScreen] bounds].size.height)
@interface GDPickerVC ()<UICollectionViewDataSource,UICollectionViewDelegateFlowLayout>
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) NSMutableArray *photoArray;
@end
@implementation GDPickerVC
- (void)viewDidLoad {
? ? [super viewDidLoad];
? ? self.view.backgroundColor = [UIColor whiteColor];
? ? self.title = @"顯示允許訪問(wèn)的圖片";
? ? //左邊返回按鈕
? ? 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];
}
//點(diǎn)擊確認(rèn)回調(diào)數(shù)據(jù)
-(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<UIImage *> *images = [NSMutableArray array];
? ? //獲取可訪問(wèn)的圖片配置選項(xiàng)
? ? PHFetchOptions *option = [[PHFetchOptions alloc] init];
? ? //根據(jù)圖片的創(chuàng)建時(shí)間升序排序返回
? ? option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
? ? //獲取類型為image的資源
? ? PHFetchResult *result = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:option];
?? ? //遍歷出每個(gè)PHAsset資源對(duì)象
? ? [resultenumerateObjectsUsingBlock:^(id? _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
? ? ? ? PHAsset*asset = (PHAsset*)obj;
? ? ? ? //將PHAsset解析為image的配置選項(xiàng)
? ? ? ? PHImageRequestOptions *requestOptions = [[PHImageRequestOptions alloc] init];
? ? ? ? //圖像縮放模式
? ? ? ? requestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
? ? ? ? //圖片質(zhì)量
? ? ? ? requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
? ? ? ? //PHImageManager解析圖片
? ? ? ? [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:requestOptions resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
? ? ? ? ? ? NSLog(@"圖片 %@",result);
? ? ? ? ? ? //在這里可以自定義一個(gè)顯示可訪問(wèn)相冊(cè)資源的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<PHAssetCollection *> *assetCollections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
? ? ? ? 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<PHAsset *> *assets = [PHAsset fetchAssetsInAssetCollection:assetCollection options:nil];
?? 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 用戶已授權(quán)此應(yīng)用程序進(jìn)行有限照片庫(kù)訪問(wèn)(iOS14新增)的適配,下面在來(lái)說(shuō)一下適配允許訪問(wèn)所有圖片驳庭,下面是適配代碼
//ios14后使用
-(void)P_enterPHPickerViewController{
? ? MJWeakSelf;
? ? //用戶選擇"允許訪問(wèn)所有照片"刑顺,調(diào)用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<PHPickerResult *> *)results
{
? ? 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<NSItemProviderReading>? _Nullable object, NSError * _Nullable error) {
?? ? ? ? ? ? ? ? if([objectisKindOfClass:UIImage.class]) {
?? ? ? ? ? ? ? ? ? ? UIImage*image = (UIImage*)object;
?? ? ? ? ? ? ? ? ? ? [weakSelfanalysisImageWithContent:image];
?? ? ? ? ? ? ? ? }
?? ? ? ? ? ? }];
?? ? ? ? }
}