iOS 保存图片到相机胶卷详解

前言:iOS 保存图片有3种原生API:
使用C函数、AssetsLibrary框架、Photos框架
目前常使用Photos框架,它从 iOS 8 被苹果推出
而AssetsLibrary框架从 iOS 9 开始已被苹果放弃
本文将主要介绍三种方法的使用和注意点

方法一:使用C函数 保存图片(简单)


  • 方法名:UIImageWriteToSavedPhotosAlbum
    只能保存图片到相册,不能自定义照片分组,使用方便,看需求
// 保存图片
- (IBAction)save:(id)sender {
    UIImageWriteToSavedPhotosAlbum(self.imageView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}

- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{
    BQFunc();
    if (error) {
        [SVProgressHUD showErrorWithStatus:@"保存失败!"];
    } else {
        [SVProgressHUD showSuccessWithStatus:@"保存成功!"];
    }
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [SVProgressHUD dismiss];
    });
}

方法二:AssetsLibrary框架(iOS9开始已过期)


  • ALASSetsGroupType: 类型
ALAssetsGroupLibrary//从iTunes来的相册内容(如本身自带的向日葵照片)。
ALAssetsGroupAlbum//设备自身产生或从iTunes同步来的照片,但是不包括照片流跟分享流中的照片。(例如从各个软件中保存下来的图片)ALAssetsGroupEvent相机接口事件产生的相册
ALAssetsGroupFaces//脸部相册(具体不清楚)
ALAssetsGroupSavedPhotos//相机胶卷照片
ALAssetsGroupPhotoStream//照片流
ALAssetsGroupAll//除了ALAssetsGroupLibrary上面所的内容
  • AssetsLibrary获取文件夹:
[self.imagePickerController.assetsLibrary enumerateGroupsWithTypes:[typeunsignedIntegerValue]
                                                       usingBlock:^(ALAssetsGroup*assetsGroup,BOOL*stop) {
                                                       }failureBlock:^(NSError*error) {
                                                           NSLog(@"Error: %@", [errorlocalizedDescription]);
                                                       }];
  • 获取图片:
[self.assetsGroup enumerateAssetsUsingBlock:^(ALAsset*result,NSUIntegerindex,BOOL*stop) {
if(result) {
    [assetsaddObject:result];
 }
}];

[self.assetsGroup enumerateAssetsAtIndexes:indexes options:0 usingBlock:^(ALAsset*result,NSUIntegerindex,BOOL*stop) {
}];

方法三:Photos框架(重点)


1.Photos框架须知:

  • 1.PHAsset : 一个PHAsset对象就代表相册中的一张图片或者一个视频
    1> 查 : [PHAsset fetchAssets...]
    2> 增删改 : PHAssetChangeRequest(包括图片\视频相关的所有改动操作)

  • 2.PHAssetCollection : 一个PHAssetCollection对象就代表一个相册
    1> 查 : [PHAssetCollection fetchAssetCollections...]
    2> 增删改 : PHAssetCollectionChangeRequest(包括相册相关的所有改动操作)

  • 3.对相片\相册的任何【增删改】操作,都必须放到以下方法的block中执行
    -[PHPhotoLibrary performChanges:completionHandler:]
    -[PHPhotoLibrary performChangesAndWait:error:]

2.保存图片到相机胶卷:

  • 先导入框架:
#import <Photos/Photos.h>
  • 异步保存方法:
    // 异步执行修改操作
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        [PHAssetChangeRequest creationRequestForAssetFromImage:self.imageView.image];
    } completionHandler:^(BOOL success, NSError * _Nullable error) {
        if (error) {
            [SVProgressHUD showErrorWithStatus:@"保存失败!"];
        } else {
            [SVProgressHUD showSuccessWithStatus:@"保存成功!"];
        }
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [SVProgressHUD dismiss];
        });
    }];
  • 同步保存方法:
    // 同步执行修改操作
    NSError *error = nil;
    [[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
        [PHAssetChangeRequest creationRequestForAssetFromImage:self.imageView.image];
    } error:&error];
    if (error) {
        [SVProgressHUD showErrorWithStatus:@"保存失败!"];
    } else {
        [SVProgressHUD showSuccessWithStatus:@"保存成功!"];
    }
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [SVProgressHUD dismiss];
    });

3.创建自定义相册:

#pragma mark - 获得当前App对应的自定义相册/保存的图片

/**
 获取当前App对应的自定义相册
 */
- (PHAssetCollection *)createdCollection {
    NSString *title = [NSBundle mainBundle].infoDictionary[(__bridge NSString *)kCFBundleNameKey];   // 获得app名
    
    PHFetchResult<PHAssetCollection *> *collections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];    //抓取所有的自定义相册
    
    for (PHAssetCollection *collection in collections) {    // 遍历查找当前app对应的自定义相册
        if ([collection.localizedTitle isEqualToString:title]) {    //找到对应的相册:说明已经创建完毕,直接返回
            return collection;
        }
    }
    
    PHAssetCollection *createdCollection = nil;
    if (createdCollection == nil){  //自定义相册没被创建过,需要创建自定义相册
        NSError *error = nil;
        __block NSString *createdCollectionID = nil;
        
        [[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
            createdCollectionID = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title].placeholderForCreatedAssetCollection.localIdentifier;  // 创建一个自定义相册,并拿到相册的唯一标识
        } error:&error];
        if (error) {    //如果有值,则创建相册失败,直接返回
            return nil;
        }
        
        createdCollection = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[createdCollectionID] options:nil].firstObject;   //拿到唯一标识后,拿到唯一相册
    }
    return createdCollection;
}

4.获取保存在相机胶卷的这张图片(通过AssetID)

/**
 获取保存在相机胶卷的这张图片(通过AssetID)
 */
- (PHFetchResult<PHAsset *> *)createdAssets {
    // 同步执行修改操作
    NSError *error = nil;
    // 保存图片到相机胶卷
    __block NSString *assetID = nil;    //相片ID
    [[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
        assetID = [PHAssetChangeRequest creationRequestForAssetFromImage:self.imageView.image].placeholderForCreatedAsset.localIdentifier;  // 赋值ID
    } error:&error];
    
    if (error) {
        return nil;
    }
    
    // 返回相机胶卷里的该相片
    return [PHAsset fetchAssetsWithLocalIdentifiers:@[assetID] options:nil];
}

5.保存图片到自定义相册

/**
 保存图片到相册
 */
- (void)saveImageIntoAlbum {
    // 1.获取相机胶卷里的该【相片】
    PHFetchResult<PHAsset *> *createdAssets = self.createdAssets;
    if (createdAssets==nil) {
        [SVProgressHUD showErrorWithStatus:@"获取相片失败!"];
        return;
    }
    
    // 2.获得【自定义相册】
    PHAssetCollection *createdCollection = self.createdCollection;
    if (createdCollection==nil) {
        [SVProgressHUD showErrorWithStatus:@"创建/获取相册失败!"];
        return;
    }
    
    
    // 3.保存图片到【自定义相册】
    NSError *error = nil;
    [[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
        PHAssetCollectionChangeRequest *request = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:createdCollection];    // 添加刚才保存的图片到【自定义相册】
        [request insertAssets:createdAssets atIndexes:[NSIndexSet indexSetWithIndex:0]];
    } error:&error];
    if (error) {    //最后的判断
        [SVProgressHUD showErrorWithStatus:@"保存失败!"];
    } else {
        [SVProgressHUD showSuccessWithStatus:@"保存成功!"];
    }
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [SVProgressHUD dismiss];
    });
}

6.请求和访问相册权限

// save点击事件:保存图片至相册胶卷
- (IBAction)save:(id)sender {
    PHAuthorizationStatus oldStatus = [PHPhotoLibrary authorizationStatus];
    // 请求/检查访问权限:
    // 如果用户还没有做出选择,会自动弹框,用户对弹框做出选择后,才会调用block
    // 如果之前已经做过选择,会直接执行block
    [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
        BQLog(@"%zd",status);
        dispatch_async(dispatch_get_main_queue(), ^{
            if (status == PHAuthorizationStatusDenied && oldStatus != PHAuthorizationStatusNotDetermined) { //用户拒绝当前App访问相册
                BQLog(@"提醒用户打开开关");
                [SVProgressHUD showErrorWithStatus:@"无系统权限访问"];
            } else if (status == PHAuthorizationStatusAuthorized){  // 用户允许当前App访问相册
                [self saveImageIntoAlbum];
            } else if (status == PHAuthorizationStatusRestricted){  // 无法访问相册
                [SVProgressHUD showErrorWithStatus:@"因系统原因,无法访问相册"];
            }
        });
    }];
}

感谢观看,欢迎点赞关注互相交流技术问题~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352