目錄
使用的庫
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];
}