iOS保存图片到相册

在APP中点击照片,都会显示出大图,然后在大图的上面会有个保存照片的按钮,照片直接保存到了系统的【相机胶卷】相册中,但是因为公司产品的需要,我们需要创建和APP同名的相册保存在【自定义相册】里面,这也就是分应用存储,因为用户可能从QQ,微信,微博分别存储,下次用户想找某个应用的图片,直接点开该应用相册即可,提高用户体验。
注意:
【自定义相册】里面的图片来源于【相机胶卷】相册中,即:【相机胶卷】引用【自定义相册】, 如果用户删掉【相机胶卷】里面的图片, 那么【自定义相册】重的图片也会删掉。但是,如果用户删掉【自定义相册】里面的图片, 那么【相机胶卷】中依然有该图片。

开发步骤:
  • 1.先保存图片到【相机胶卷】(不能直接保存到自定义相册中)
  • 2.拥有一个【自定义相册】
  • 3.将刚才保存到【相机胶卷】里面的图片引用到【自定义相册】

方法1:用C语言函数实现

将图片保存到系统的相册中,只需要下面两句代码就搞定了

//参数1:图片对象
//参数2:成功方法绑定的target
//参数3:成功后调用方法
//参数4:需要传递信息(成功后调用方法的参数)
UIImageWriteToSavedPhotosAlbum(self.imageView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
#pragma mark -- <保存到相册>
-(void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    NSString *msg = nil ;
    if(error){
        msg = @"保存图片失败" ;
    }else{
        msg = @"保存图片成功" ;
    }
}

注意:UIImageWriteToSavedPhotosAlbum方法必须实现图1的代理方法,否则会崩溃。

1.png

界面效果如下:界面图


效果图.png
方法2:使用Photos框架实现
2.1 Photos01-基本认识

PHAsset : 一个PHAsset对象就代表相册中的一张图片或者一个视频
PHAssetCollection : 一个PHAssetCollection 对象就代表一个相册

如果我们想保存图片到【相机胶卷】,首先要保证添加一个新的PHAsset对象,那么如何操作这些对象呢?无非是对这些对象进行增删改查。

PHAsset 一个PHAsset对象就代表相册中的一张图片或者一个视频
增删改 PHAssetChangeRequest 包括图片/视频相关的改动操作

[PHAsset fetchAssets...];

PHAssetCollection 一个PHAssetCollection 对象就代表一个相册

增删改 PHAssetCollectionChangeRequest 包括相册相关的所有改动操作

[PHAssetCollection fetchAssetCollectionsContainingAsset:...];

2.2 Photos02-保存图片到相机胶卷
    //保存图片到【相机胶卷】
    /// 异步执行修改操作
    [[PHPhotoLibrary sharedPhotoLibrary]performChanges:^{
        [PHAssetChangeRequest creationRequestForAssetFromImage:self.imageView.image];
    } completionHandler:^(BOOL success, NSError * _Nullable error) {
        if (error) {
            NSLog(@"%@",@"保存失败");
        } else {
            NSLog(@"%@",@"保存成功");
        }
    }];

效果如图2 图3所示


2.png
3.png

Tips:
如果直接使用 [PHAssetChangeRequest creationRequestForAssetFromImage:self.imageView.image]; 则会出现如下的崩溃信息

reason: 'This method can only be called from inside of -[PHPhotoLibrary performChanges:completionHandler:] or -[PHPhotoLibrary performChangesAndWait:error:]'

结论:凡是涉及增删改的操作,均需要放在performChanges里面执行。

 [[PHPhotoLibrary sharedPhotoLibrary]performChanges:^{
       // 这里执行操作
    } completionHandler:^(BOOL success, NSError * _Nullable error) {
    }];
2.3 Photos03-创建新的相册
    NSError *error = nil;
    [[PHPhotoLibrary sharedPhotoLibrary]performChangesAndWait:^{
        //获取app名字
        
        NSString *title = [NSBundle mainBundle].infoDictionary[(__bridge NSString*)kCFBundleNameKey];
        //创建一个【自定义相册】
        [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title];
    } error:&error];

效果如图4:


4.png

Tips:如果我们一直点击保存图片按钮,则会出现重复创建多个相册的问题:

如图5所示

5.png

接着我们来解决这个问题。

2.4 Photos04-查询相册
    NSString *title = [NSBundle mainBundle].infoDictionary[(__bridge NSString*)kCFBundleNameKey];
    //查询所有【自定义相册】
    PHFetchResult<PHAssetCollection *> *collections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
    PHAssetCollection *createCollection = nil;
    for (PHAssetCollection *collection in collections) {
        if ([collection.localizedTitle isEqualToString:title]) {
            createCollection = collection;
            break;
        }
    }
    if (createCollection == nil) {
        //当前对应的app相册没有被创建
        //创建一个【自定义相册】
        NSError *error = nil;
        [[PHPhotoLibrary sharedPhotoLibrary]performChangesAndWait:^{
            //创建一个【自定义相册】
            [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title];
        } error:&error];

    }

输出结果:
此时,无论你点击多少次,均只会创建一个与APP同名的相册。

Tips: 如果我们想把图片保存到【自定义相册】里面的话,必须拿到相册对象,但是细心地你此时可能会发现,如果是第一次创建相册(即:相册中没有与APP同名的相册),createCollection对象会为nil,所以我们还需要优化创建【自定义相册】的代码。

核心代码:

    if (createCollection == nil) {
        //当前对应的app相册没有被创建
        //创建一个【自定义相册】
        NSError *error = nil;
        __block NSString *createCollectionID = nil;
        [[PHPhotoLibrary sharedPhotoLibrary]performChangesAndWait:^{
            //创建一个【自定义相册】
           createCollectionID = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title].placeholderForCreatedAssetCollection.localIdentifier;
        } error:&error];
        createCollection = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[createCollectionID] options:nil].firstObject;
    }
    NSLog(@"%@",createCollection);

输出结果:

2018-08-12 12:31:59.198751+0800 SavePhotoDemo[971:36240] <PHAssetCollection: 0x7fde6dc33960> 7C7FBA58-54AA-4320-9B2A-593BB75428FB/L0/040, title:"SavePhotoDemo", subtitle:"(null)" assetCollectionType=1/2
2.5 Photos05-保存图片到自定义相册

我们创建完相册之后,需要将【相机胶卷】中的图片放到【自定义相册】里面去。
核心代码如下:

    // 1.先保存图片到【相机胶卷】
    /// 同步执行修改操作
    NSError *error = nil;
    __block PHObjectPlaceholder *placeholder = nil;
    [[PHPhotoLibrary sharedPhotoLibrary]performChangesAndWait:^{
       placeholder =  [PHAssetChangeRequest creationRequestForAssetFromImage:self.imageView.image].placeholderForCreatedAsset;
    } error:&error];
    if (error) {
        NSLog(@"保存失败");
        return;
    }
    // 2.拥有一个【自定义相册】
    PHAssetCollection * assetCollection = self.createCollection;
    if (assetCollection == nil) {
        NSLog(@"创建相册失败");
    }
    // 3.将刚才保存到【相机胶卷】里面的图片引用到【自定义相册】
    [[PHPhotoLibrary sharedPhotoLibrary]performChangesAndWait:^{
        PHAssetCollectionChangeRequest *requtes = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:assetCollection];
        [requtes addAssets:@[placeholder]];
    } error:&error];
    if (error) {
        NSLog(@"保存图片失败");
    } else {
        NSLog(@"保存图片成功");
    }

此时,我们点击保存图片到新建相册~按钮的时候,改图片变被保存到【相机胶卷】和【自定义相册】里面了。
如图6所示

6.png

但是,此时你会发现,每保存新的图片,自定义相册中均会添加到后面,那么如何把每次新添加的图片前置呢?我们发现【相机胶卷】中每次新的图片均会作为封面,系统自带的这个比较智能,如图图7所示


7.png

所以修改添加图片到相册的代码

    // 3.将刚才保存到【相机胶卷】里面的图片引用到【自定义相册】
    [[PHPhotoLibrary sharedPhotoLibrary]performChangesAndWait:^{
        PHAssetCollectionChangeRequest *requtes = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:assetCollection];
        [requtes insertAssets:@[placeholder] atIndexes:[NSIndexSet indexSetWithIndex:0]];
    } error:&error];
    

此时,【自定义相册】中新添加的图片便会作为相册的封面了,效果如图8所示。

8.png

Demo下载

推荐阅读更多精彩内容