目录
- 使用的库
- 针对系统相册的操作
- 请求相册访问权限
- 获取当前的系统相册列表
- 判断系统相册是否存指定文件夹
- 创建相册目录
- 保存图片到指定的目录
- 保存视频到指定的相册中
- 删除指定目录下的文件
- 保存照片视频到系统相册
- 检测当前资源是否为云存储
- 加载相册中的照片/视频
使用的库
ios8 以前,使用的是<AssetsLibrary/AssetsLibrary.h>
,ios8以后,苹果推出了<Photos/Photos.h>
现在一般使用photos库
针对系统相册的操作
文件处理思路,我们保存到系统相册还比较好处理,但是删除就有点麻烦了,因为我们存的文件名到了系统相册名称就已经变了,文件名也没有对外提供,但是提供了一个
localIdentifier
的字段,用来标识相册中的唯一元素,那么我们就需要将这个字段保存到plist或数据库,通过对应关系找到就可以进行删除了。
对相册的任何改变操作,都应该在
- (void)performChanges:(dispatch_block_t)changeBlock completionHandler:(nullable void(^)(BOOL success, NSError *__nullable error))completionHandler;
的changeBlock方法内部做处理
请求相册访问权限
plist 中需要配置的key
- NSPhotoLibraryUsageDescription --- Privacy - Photo Library Usage Description
typedef NS_ENUM(NSInteger, PHAuthorizationStatus) {
PHAuthorizationStatusNotDetermined = 0,
// 用户还没有对这个应用程序作出选择
PHAuthorizationStatusRestricted,
// 此应用程序未被授权访问照片数据.用户无法更改当前应用程序的状态
PHAuthorizationStatusDenied,
// 用户明确拒绝了应用程序对照片数据的访问。
PHAuthorizationStatusAuthorized,
// 用户已授权此应用程序访问照片数据。
PHAuthorizationStatusLimited API_AVAILABLE(ios(14)),
// 用户已经授权使用有限的图片库.
// 添加PHPhotoLibraryPreventAutomaticLimitedAccessAlert = YES到应用程序的信息。防止plist自动提醒用户更新有限的库选择
// 使用<PhotosUI/PHPhotoLibrary+PhotosUISupport.h>下的[PHPhotoLibrary(PhotosUISupport) presentLimitedLibraryPickerFromViewController:]来获取有限的库选择器
};
+ (BOOL)requestPhotoPrivacy
{
// 获取授权状态
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
if (status == PHAuthorizationStatusDenied) {
// 如果拒绝做对应弹框处理
return NO;
}
return YES;
}
获取当前的系统相册列表
对应的相册名
- Favorites 收藏夹
- Recents 最近
- Recently Added 最近添加
- Videos 视频
- Panoramas 全景照片
- Time-lapse 延时摄影
- Slo-mo 慢动作
- Screenshots 截屏
- Portrait 人像
- Live Photos 实时照片
- Selfies 自拍
- Animated 动画
- Long Exposure 长曝光
- Recently Deleted 最近删除
PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
[smartAlbums enumerateObjectsUsingBlock:^(PHAssetCollection * _Nonnull collection, NSUInteger idx, BOOL * _Nonnull stop) {
if ([collection isKindOfClass:[PHAssetCollection class]]) {
PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:nil];
NSLog(@"%@,count = %lu",collection.localizedTitle,(unsigned long)fetchResult.count);
}
}];
判断系统相册是否存指定文件夹
- (BOOL)isExistFolder:(NSString *)folderName {
//首先获取用户手动创建相册的集合
PHFetchResult *collectonResuts = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
__block BOOL isExisted = NO;
//对获取到集合进行遍历
[collectonResuts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
PHAssetCollection *assetCollection = obj;
//folderName是我们写入照片的相册
if ([assetCollection.localizedTitle isEqualToString:folderName]) {
isExisted = YES;
}
}];
return isExisted;
}
创建相册目录
- (void)createFolder:(NSString *)folderName {
if (![self isExistFolder:folderName]) {
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
//添加HUD文件夹
[PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:folderName];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
if (success) {
NSLog(@"创建相册文件夹成功!");
} else {
NSLog(@"创建相册文件夹失败:%@", error);
}
}];
}
}
保存图片到指定的目录
- (void)saveImagePath:(NSString *)imagePath{
NSURL *url = [NSURL fileURLWithPath:imagePath];
//标识保存到系统相册中的标识
__block NSString *localIdentifier;
//首先获取相册的集合
PHFetchResult *collectonResuts = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
//对获取到集合进行遍历
[collectonResuts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
PHAssetCollection *assetCollection = obj;
//Camera Roll是我们写入照片的相册
if ([assetCollection.localizedTitle isEqualToString:_folderName]) {
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
//请求创建一个Asset
PHAssetChangeRequest *assetRequest = [PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:url];
// 设置创建时间
assetRequest.creationDate = [NSDate date];
//请求编辑相册
PHAssetCollectionChangeRequest *collectonRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:assetCollection];
//为Asset创建一个占位符,放到相册编辑请求中
PHObjectPlaceholder *placeHolder = [assetRequest placeholderForCreatedAsset];
//相册中添加照片
[collectonRequest addAssets:@[placeHolder]];
localIdentifier = placeHolder.localIdentifier;
} completionHandler:^(BOOL success, NSError *error) {
if (success) {
NSLog(@"保存图片成功!");
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:[self readFromPlist]];
[dict setObject:localIdentifier forKey:[self showFileNameFromPath:imagePath]];
[self writeDicToPlist:dict];
} else {
NSLog(@"保存图片失败:%@", error);
}
}];
}
}];
}
保存视频到指定的相册中
- (void)saveVideoPath:(NSString *)videoPath {
NSURL *url = [NSURL fileURLWithPath:videoPath];
//标识保存到系统相册中的标识
__block NSString *localIdentifier;
//首先获取相册的集合
PHFetchResult *collectonResuts = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
//对获取到集合进行遍历
[collectonResuts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
PHAssetCollection *assetCollection = obj;
//folderName是我们写入照片的相册
if ([assetCollection.localizedTitle isEqualToString:_folderName]) {
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
//请求创建一个Asset
PHAssetChangeRequest *assetRequest = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:url];
//请求编辑相册
PHAssetCollectionChangeRequest *collectonRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:assetCollection];
//为Asset创建一个占位符,放到相册编辑请求中
PHObjectPlaceholder *placeHolder = [assetRequest placeholderForCreatedAsset];
//相册中添加视频
[collectonRequest addAssets:@[placeHolder]];
localIdentifier = placeHolder.localIdentifier;
} completionHandler:^(BOOL success, NSError *error) {
if (success) {
NSLog(@"保存视频成功!");
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:[self readFromPlist]];
[dict setObject:localIdentifier forKey:[self showFileNameFromPath:videoPath]];
[self writeDicToPlist:dict];
} else {
NSLog(@"保存视频失败:%@", error);
}
}];
}
}];
}
删除指定目录下的文件
- (void)deleteFile:(NSString *)filePath {
// 判断目录是否存在
if ([self isExistFolder:_folderName]) {
//获取需要删除文件的localIdentifier
NSDictionary *dict = [self readFromPlist];
NSString *localIdentifier = [dict valueForKey:[self showFileNameFromPath:filePath]];
// 获取相册列表
PHFetchResult *collectonResuts = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
// 遍历查找指定的相册
[collectonResuts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
PHAssetCollection *assetCollection = obj;
if ([assetCollection.localizedTitle isEqualToString:_folderName]) {
// 获取对应相册下的照片列表
PHFetchResult *assetResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:[PHFetchOptions new]];
// 遍历寻找对应的标识
[assetResult enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
PHAsset *asset = obj;
if ([localIdentifier isEqualToString:asset.localIdentifier]) {
// 开始对相册改变
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
// 删除对应的照片
[PHAssetChangeRequest deleteAssets:@[obj]];
} completionHandler:^(BOOL success, NSError *error) {
if (success) {
NSLog(@"删除成功!");
NSMutableDictionary *updateDic = [NSMutableDictionary dictionaryWithDictionary:dict];
[updateDic removeObjectForKey:[self showFileNameFromPath:filePath]];
[self writeDicToPlist:updateDic];
} else {
NSLog(@"删除失败:%@", error);
}
}];
}
}];
}
}];
}
}
保存照片视频到系统相册
- (BOOL)saveVideoToSystemMediaLibrary:(NSURL *)url AssetId:(NSString *)assetId {
NSError *error = nil;
BOOL result = [[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
// 创建改变请求
PHAssetChangeRequest *assetRequest = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:url];
assetRequest.creationDate = [NSDate date];
PHObjectPlaceholder *placeHolder = [assetRequest placeholderForCreatedAsset];
// 在数据库中建立 自定义标识符和系统生成标识符关联关系
[self.dataBase saveLocalIdentifier:placeHolder.localIdentifier WithAssetsId:assetId];
} error:&error];
// 移除对应的资源文件
[[NSFileManager defaultManager]removeItemAtURL:url error:nil];
return result && !error;
}
- (BOOL)saveImageToSystemMediaLibrary:(NSURL *)url AssetId:(NSString *)assetId {
// 判断是否已经下载过对应的图片资源
if (![self isDonwloadWithAssetId:assetId]) {
NSError *error = nil;
BOOL result = [[PHPhotoLibrary sharedPhotoLibrary]performChangesAndWait:^{
// 创建添加图片请求
PHAssetChangeRequest *assetRequest = [PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:url];
assetRequest.creationDate = [NSDate date];
PHObjectPlaceholder *placeHolder = [assetRequest placeholderForCreatedAsset];
// 建立关联关系
[self.dataBase saveLocalIdentifier:placeHolder.localIdentifier WithAssetsId:assetId];
} error:&error];
// 移除对应的资源
[[NSFileManager defaultManager]removeItemAtURL:url error:nil];
return result && !error;
} else {
[[NSFileManager defaultManager]removeItemAtURL:url error:nil];
return YES;
}
}
- (BOOL)isDonwloadWithAssetId:(NSString *)assetId {
// 获取对应的标识符
NSString *localIdentifier = [self.dataBase localIdentifierWithAssetId:assetId];
if (!localIdentifier.length) {
return NO;
}
// 查找是否存在对应的资源
PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifier] options:nil];
if (result.count) {
return YES;
}
return NO;
}
检测当前资源是否为云存储
+ (void)checkSystemAssetInCloud:(PHAsset *)asset complet:(void(^)(BOOL isInCloud))complet {
//视频类型
if (asset.mediaType == PHAssetMediaTypeVideo) {
// 请求视频资源
[[PHImageManager defaultManager] requestExportSessionForVideo:asset options:nil exportPreset:AVAssetExportPresetHighestQuality resultHandler:^(AVAssetExportSession * _Nullable exportSession, NSDictionary * _Nullable info) {
// 获取的info中 PHImageResultIsInCloudKey 对应是否云存储
dispatch_async(dispatch_get_main_queue(), ^{
complet ? complet([info[PHImageResultIsInCloudKey] intValue] && !exportSession) : nil;
});
}];
}
// 图片类型
if (asset.mediaType == PHAssetMediaTypeImage) {
// 创建图片请求的option
PHImageRequestOptions *originOptions = [[PHImageRequestOptions alloc]init];
// 初始化相关的值
originOptions.version = PHImageRequestOptionsVersionOriginal;
originOptions.resizeMode = PHImageRequestOptionsResizeModeNone;
originOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
// ios 13之后
if (@available(iOS 13, *)) {
// 通过option请求图片资源
[[PHImageManager defaultManager]requestImageDataAndOrientationForAsset:asset options:originOptions resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, CGImagePropertyOrientation orientation, NSDictionary * _Nullable info) {
// 从info中获取是否云存储值
dispatch_async(dispatch_get_main_queue(), ^{
complet ? complet([info[PHImageResultIsInCloudKey] intValue] && !imageData) : nil;
});
}];
} else {
// 13之前
[[PHImageManager defaultManager]requestImageDataForAsset:asset options:originOptions resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
// 从info中获取对应信息
dispatch_async(dispatch_get_main_queue(), ^{
complet ? complet([info[PHImageResultIsInCloudKey] intValue] && !imageData) : nil;
});
}];
}
}
}
加载相册中的照片/视频
- (void)loadPhotosFromAlbum
{
__weak AlbumViewController *albumVC = self;
// 指定要加载的资源类型
PHAssetMediaType assetType = PHAssetMediaTypeImage;
if (self.type == MediaTypeVideo) {
assetType = PHAssetMediaTypeVideo;
}
// 获取对应的资源列表
assetsFetchResult = [PHAsset fetchAssetsWithMediaType:assetType options:nil];
// 遍历资源对象
[assetsFetchResult enumerateObjectsUsingBlock:^(PHAsset *obj, NSUInteger idx, BOOL * _Nonnull stop) {
// 转换字符串
NSString *createTimeString = [Utils compareDate:obj.creationDate];
// 转换模型
AlbumPhotoModel *photoModel = [[AlbumPhotoModel alloc] init];
photoModel.isShowVideoBtn = NO;
photoModel.createTime = createTimeString;
photoModel.identifier = obj.localIdentifier;
photoModel.importTime = [Utils compareDate:[Utils timeZoneToDate:[NSDate date]]];
photoModel.importDate = [Utils timeZoneToDate:[NSDate date]];
photoModel.asset = obj;
photoModel.selected = NO;
// 根据创建时间字符串分类存储
NSMutableArray *data = [albumVC.assetDic objectForKey:createTimeString];
if (data == nil) { //6AA5251E-A38E-4B96-99B0-DCABD8ACE0D3/L0/001
data = [NSMutableArray array];
MediaSectionModel *mediaSectionModel = [[MediaSectionModel alloc] init];
mediaSectionModel.sectionTimeString = createTimeString;
mediaSectionModel.currentSectionState = false;
mediaSectionModel.sectionTime = obj.creationDate;
[albumVC.timeSections addObject:mediaSectionModel];
[albumVC.assetDic setObject:data forKey:createTimeString];
}
[data addObject:photoModel];
[albumVC.assets addObject:photoModel];
}];
//按时间逆序排序
// self.timeSections = [[[albumVC.timeSections reverseObjectEnumerator] allObjects] mutableCopy];
self.timeSections = [[Utils sortArray:self.timeSections withKey:@"sectionTime" ascending:false] mutableCopy];
self.collectionView.sectionTitles = albumVC.timeSections;
self.collectionView.albumDic = albumVC.assetDic;
[self.collectionView reloadData];
}