实际上,自定义相册中的图片并不是实际的图片,而是对系统【相机胶卷】这个相册中的图片进行一个引用,所以将图片保存到自定义相册的第一步就是先保存到系统的【相机胶卷】中...
• 将图片保存到系统相册【相机胶卷】中
(1)C语言函数来保存
(2)AssetsLibrary框架--系统自带,iOS9废弃
(3)Photos框架--系统自带,iOS8即可使用,取代AssetsLibrary
• 是否存在自定义相册(没有则创建)将图片添加到自定义相册
AssetsLibrary
Photos
2.1 重要的类
该框架有几个非常重要的类:PHAsset、PHAssetCollection 和 PHLibrary。
• PHAsset 表示一个图片或者视频文件(存储在手机的照片 APP 中的)。与具体图片有关的使用这个类
• PHAssetCollection 表示图片集合或者视频集合,其实就是指相册(包括系统相册和自定义相册)
• PHLibrary 表示整个相册库,包括整个相册和图片等
注:只要与单个图片相关,使用 PHAsset。只要与相册相关,使用 PHAssetCollection
2.2 查询操作
查询操作,直接使用 PHAsset 和 PHAssetCollection 类本身的方法
// 获取相册中的图片--传入相册图片的ID---返回一组图片
[PHAsset fetchAssetsWithLocalIdentifiers:@[ID] options:nil];
// 查询手机中所有的相册列表(分为系统相册和自定义相册,通过控制传入的参数来确定)---返回相册组--类似数组-forin遍历即可
[PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
2.3 增删改操作
1.如果做查询之外的操作,比如说保存图片、创建自定义相册、向自定义相册中添加图片等,都需要使用另外两个类:PHAssetChangeRequest 和 PHAssetCollectionChangeRequest
2.这些操作必须在 [[PHPhotoLibrary sharedPhotoLibrary] performChange...]的 block 中间调用
3.1 C语言函数
保存操作的代码:
// 把图片保存到系统相册中,结束后调用 image:didFinishSavingWithError:contextInfo:方法(回调方法)
// 回调方法的格式有要求,可以进入头文件查看
UIImageWriteToSavedPhotosAlbum(self.imageView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
实现回调方法
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
if (error) {
NSLog(@"保存图片失败!");
return;
}
NSLog(@"保存图片成功!");
}
3.2 Photos 框架保存图片到系统相册
Photos 框架保存图片 --- 使用 PHAssetChangeRequest 类方法
有两种方式:异步方式和同步方式,但是保存图片的操作性能消耗不大,所以可以直接使用同步方式
3.2.1 异步方式
- (void)asyncSaveImageWithPhotos {
// 必须在 block 中调用
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
// 异步执行保存图片操作
[PHAssetChangeRequest creationRequestForAssetFromImage:self.imageView.image];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
// 保存结束后,回调
if (error) {
[[AppDelegate app] showErrorAndAutoHide:@"保存失败!"];
} else {
[[AppDelegate app] showSuccessAndAutoHide:@"保存成功!"];
}
}];
}
3.2.2 同步方式
- (PHFetchResult *)synchronousSaveImageWithPhotosWithImage:(UIImage *)image {
__block NSString *createdAssetId = nil;
// 添加图片到【相机胶卷】
[[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
createdAssetId = [PHAssetChangeRequest creationRequestForAssetFromImage:image].placeholderForCreatedAsset.localIdentifier;
} error:nil];
if (createdAssetId == nil) return nil;
// 在保存完毕后取出图片
return [PHAsset fetchAssetsWithLocalIdentifiers:@[createdAssetId] options:nil];
}
如果没有,则常见跟当前 APP 同名的自定义相册
实现思路:
① 获取当前项目的名字
NSString *titlePhotos = [NSBundle mainBundle].infoDictionary[(NSString *)kCFBundleNameKey];
② 使用PHAssetCollection的fetchAssetCollectionsWithType:subType:options方法,通过传入类型,获取所有的自定义相册列表
③ 遍历获取的自定义相册列表,与APP的名称进行比对,匹配后返回当前同名的自定义相册 PHAssetCollection对象
④ 如果没有找到,则开始创建,在PHPhotoLibrary单例对象的perfromChange方法中执行创建自定义相册操作
⑤ block中,保存待创建相册的占位标识符---其实这个时刻,相册根本没创建完成
⑥ 通过error判断是否创建成功
⑦ 如果创建成功,通过PHAssetCollection的fetchAssetCollectionsWithLocalIdentifiers:options来获取当前创建相册对象PHAssetCollection
- (PHAssetCollection *)getAssetCollectionWithAppNameAndCreateCollection {
NSString *titlePhotos = [NSBundle mainBundle].infoDictionary[(NSString *)kCFBundleNameKey];
// 获得所有的自定义相册
PHFetchResult *collections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
for (PHAssetCollection *collection in collections) {
if ([collection.localizedTitle isEqualToString:titlePhotos]) {
return collection;
}
}
NSError *error = nil;
// 代码执行到这里,说明还没有自定义相册
__block NSString *createdCollectionId = nil;
// 创建一个新的相册
[[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
createdCollectionId = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:titlePhotos].placeholderForCreatedAssetCollection.localIdentifier;
} error:&error];
if (error) {
[[AppDelegate app] showErrorAndAutoHide:@"创建失败!"];
return nil;
} else {
[[AppDelegate app] showSuccessAndAutoHide:@"创建成功!"];
// 创建完毕后再取出相册
return [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[createdCollectionId] options:nil].firstObject;
}
}
实现思路
① 使用获取的自定义相册来创建PHAssetCollection对象
② 将刚才保存到系统相册的PHAsset保存到相册中
- (void)saveImageToCustomAblumWithImage:(UIImage *)image {
// 将图片保存到系统的【相机胶卷】中---调用刚才的方法
PHFetchResult *assets = [self synchronousSaveImageWithPhotosWithImage:image];
if (assets == nil) {
[[AppDelegate app] showErrorAndAutoHide:@"保存失败!"];
return;
}
// 拥有自定义相册(与 APP 同名,如果没有则创建)--调用刚才的方法
PHAssetCollection *assetCollection = [self getAssetCollectionWithAppNameAndCreateCollection];
if (assetCollection == nil) {
[[AppDelegate app] showErrorAndAutoHide:@"相册创建失败!"];
return;
}
// 将刚才保存到相机胶卷的图片添加到自定义相册中 --- 保存带自定义相册--属于增的操作,需要在PHPhotoLibrary的block中进行
NSError *error = nil;
[[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
//--告诉系统,要操作哪个相册
PHAssetCollectionChangeRequest *collectionChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:assetCollection];
//--添加图片到自定义相册--追加--就不能成为封面了
//--[collectionChangeRequest addAssets:assets];
//--插入图片到自定义相册--插入--可以成为封面
[collectionChangeRequest insertAssets:assets atIndexes:[NSIndexSet indexSetWithIndex:0]];
} error:&error];
if (error) {
[[AppDelegate app] showErrorAndAutoHide:@"保存失败!"];
return;
}
[[AppDelegate app] showSuccessAndAutoHide:@"保存成功!"];
}
当第一次使用APP的时候,或者第一次访问相册的时候,系统会弹出授权选择对话框询问用户。所以我们在保存图片到相册的时候,需要判断当前是否授权。
6.1 权限分类
PHAuthorizationStatusNotDetermined ,---用户之前还未决定
PHAuthorizationStatusRestricted, ---系统问题,用户没有权限决定--比如家长控制器模式
PHAuthorizationStatusDenied,---用户之前拒绝过
PHAuthorizationStatusAuthorized --用户允许
6.2 请求权限的方式
可以使用NSPhotoLibrary的类方法requestAuthorization来查看权限,或者请求权限。如果用户之前没做决定,则弹出系统对话框请求权限;如果用户做过决定,则调用该类方法的block。
保存图片到自定义相册操作代码:
- (void)savePhtotsWithImage:(UIImage *)image {
// 获取当前的授权状态
PHAuthorizationStatus lastStatus = [PHPhotoLibrary authorizationStatus];
// 请求授权
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
//回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
if(status == PHAuthorizationStatusDenied) {
if (lastStatus == PHAuthorizationStatusNotDetermined) {
//说明,用户之前没有做决定,在弹出授权框中,选择了拒绝
[[AppDelegate app] showErrorAndAutoHide:@"保存失败!"];
return;
}
// 说明,之前用户选择拒绝过,现在又点击保存按钮,说明想要使用该功能,需要提示用户打开授权
[[AppDelegate app] showErrorAndAutoHide:@"失败!请打开 设置-隐私-照片 来进行设置"];
} else if(status == PHAuthorizationStatusAuthorized) {
//保存图片---调用上面封装的方法
[self saveImageToCustomAblumWithImage:image];
} else if (status == PHAuthorizationStatusRestricted) {
[[AppDelegate app] showErrorAndAutoHide:@"系统原因,无法访问相册!"];
}
});
}];
}
注:项目中需要保存图片到自定义相册(整合网上资源+自定义封装)分享共同学习