iOS小技能:保存图片到相册( 监听用户的相册授权动作)

2,514 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第29天,点击查看活动详情

引言

iOS 保存图片到相册的需求:二维码界面,新增一个“保存到相册”按钮,点击完成将核心视图信息保存到相册 。

  1. 监听到用户点击不允许,不允许时显示引导
  2. 用户未作出明确选择的情况下自己主动请求了一次权限设置(刷新UI的代码放到主线程执行)

预备知识:

  • iOS小技能:iOS14相册权限适配 (Limited Photo Library Access)blog.csdn.net/z929118967/…

  • NSFileManager类对文件的操作需求背景: 切换机器人时,备份iOSapp的沙盒文件和目录

I 保存图片

    //参数1:图片对象
    //参数2:成功方法绑定的target
    //参数3:成功后调用方法
    //参数4:需要传递信息(成功后调用方法的参数) 一般写nil
    UIImageWriteToSavedPhotosAlbum(self.imageView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);

需求: 码牌类设备(含电子立牌、静态码牌、码牌喇叭一体机)增加收款二维码查看和下载功能

1.1 直接保存图片对象

- (IBAction)saveClick:(UIButton *)sender {
    //参数1:图片对象
    //参数2:成功方法绑定的target
    //参数3:成功后调用方法
    //参数4:需要传递信息(成功后调用方法的参数) 一般写nil
    UIImageWriteToSavedPhotosAlbum(self.imageView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
    /* 1.先保存图片到【相机胶卷】(不能直接保存到自定义相册中)
        1> C语言函数
        2> AssetsLibrary框架  (iOS4支持,iOS9.0被废弃)
        3> Photos框架 (iOS 8.0支持 推荐使用)
       2.拥有一个【自定义相册】
        1> AssetsLibrary框架
        2> Photos框架
       3.将刚才保存到【相机胶卷】里面的图片引用到【自定义相册】
        1> AssetsLibrary框架
        2> Photos框架
     */
    
}
#pragma mark -- <保存到相册>
-(void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    NSString *msg = nil ;
    if(error){
        msg = @"保存图片失败" ;
    }else{
        msg = @"保存图片成功" ;
    }
}

1.2 保持视图到相册

保持视图到相册: 保留商户名称和商户号信息

//保存图片
- (void)saveImageToPicture {
    
    
    UIGraphicsBeginImageContextWithOptions(self.penView.canvasView.bounds.size, NO, 0.0);
    
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    [self.penView.canvasView.layer renderInContext:ctx];
    
    self.saveImage = UIGraphicsGetImageFromCurrentImageContext();
    
    UIGraphicsEndImageContext();
    
    //保存到相册
    UIImageWriteToSavedPhotosAlbum(self.saveImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);

    
    return;

}

- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
    NSString *msg = @"保存成功";
    if (error != nil) {
        msg = @"保存失败!";
    }
    
    UIAlertController *alertVc = [UIAlertController alertControllerWithTitle:@"" message:msg preferredStyle:UIAlertControllerStyleAlert];
    [alertVc addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
        
    }]];
    
    [self presentViewController:alertVc animated:YES completion:nil];
}


1.3 视图与保存的图片宽高的关系

结论:视图的宽度与屏幕的宽度越接近,图片越清晰。

保存的视图的宽,如果是和屏幕的宽一致,则是1:1的关系保存。

如果保存的视图的宽<屏幕的宽,则图片是以屏幕的宽进行放大保存,但是最终保存的图片的宽会等于屏幕的宽。

II 相册权限处理

2.1 添加权限

	<key>NSPhotoLibraryAddUsageDescription</key>
	<string>需要访问您的相册,用于收款码图片的下载</string>
	<key>NSPhotoLibraryUsageDescription</key>
	<string>App会在自定义会员的时候使用您的相册权限</string>

如果没添加对应的错误信息

#366012 SIGABRT
libsystem_kernel.dylib ___abort_with_payload

2.2 监听第一次授权时,用户点击不允许和允许

思路:

  1. 监听到用户点击不允许,不允许时显示引导
  2. 用户未作出明确选择的情况下自己主动请求了一次权限设置(刷新UI的代码放到主线程执行)
                    dispatch_sync(dispatch_get_main_queue(), ^{
                        //刷新UI的代码放到主线程执行
                        block4none(status);

                    });


用法:

    [QCTLocationServiceUtil isHasPhotoLibraryAuthorityWithisShowAlert:YES block:^(id  _Nonnull sender) {
        [weakSelf toSavedPhotosAlbum:model];
        
    }];


代码实现:



/**
 1. 监听到用户点击不允许,不允许时显示引导
 2. 用户未作出明确选择的情况下自己主动请求了一次权限设置
 showAlert:不允许时显示引导
 block: 允许之后的动作,比如保存图片
 */
+(BOOL)isHasPhotoLibraryAuthorityWithisShowAlert:(BOOL)showAlert  block:(void (^)(id sender))block
{
    PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus] ;
//1.  定义局部block:  处理没有权限的情况,显示引导
    BOOL (^block4none)(PHAuthorizationStatus ) = ^ BOOL (PHAuthorizationStatus status ){
        NSLog(@" 没有访问图库的权限==============");
        if (showAlert) {
            [LBAlertController showAlertTitle:@"无法使用相册" content:@"请在iPhone的\"设置-隐私-照片\"中允许访问照片。" cancelString:@"取消" cancleBlock:nil sureString:@"去设置" sureBlock:^{
                // 需要在info.plist中添加 URL types 并设置一项URL Schemes为prefs  IOS10 以后不起作用
                    if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]){
                    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
                }
            } currentController:[QCT_Common getCurrentVC]];
        }
        
        
        return NO;
        
    };
    switch (status) {
        case PHAuthorizationStatusRestricted:
        case PHAuthorizationStatusDenied : {
            
//没有访问图库的权限
            return block4none(status);
            

        }
            break;
            
        case PHAuthorizationStatusNotDetermined:{//2. 用户未作出明确选择的情况下自己主动请求了一次权限设置
            [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus phStatus) {
                //监听到用户点击不允许,不允许时显示引导
                if (phStatus == PHAuthorizationStatusRestricted || phStatus == PHAuthorizationStatusDenied) {
                    
                    dispatch_sync(dispatch_get_main_queue(), ^{
                        //刷新UI的代码放到主线程执行
                        block4none(status);

                    });

                    

                } else if (phStatus == PHAuthorizationStatusNotDetermined) {
                    
                    // 不处理
                } else {
                    // 执行外围的block
//                    status = QMUIAssetAuthorizationStatusAuthorized;
                    
                    if(block){//执行允许之后的保存图片操作
                        block(nil);
                    }

                }
                
            }];

            return  NO;
            
        }
            
        default:
            break;
    }
    if(block){// 3. 执行允许之后的保存图片操作
        block(nil);
    }
    return  YES;
}


2.3 简单的做权限校验(不推荐)

在这里插入图片描述

  • 用法
// 检测摄像头
    if(![QCTLocationServiceUtil isHasCameraAuthorityWithisShowAlert:YES]){
        return ;
    }

        // 做相册权限校验
        
        if(![QCTLocationServiceUtil isHasPhotoLibraryAuthorityWithisShowAlert:YES]){
            return ;
        }


  • 做相册权限校验
+(BOOL)isHasPhotoLibraryAuthorityWithisShowAlert:(BOOL)showAlert
{
    ALAuthorizationStatus status = [ALAssetsLibrary authorizationStatus];
    if (status == ALAuthorizationStatusRestricted || status == ALAuthorizationStatusDenied) {
        NSLog(@"LBLog 没有访问图库的权限==============");
        if (showAlert) {
            [LBAlertController showAlertTitle:@"无法使用相册" content:@"请在iPhone的\"设置-隐私-照片\"中允许访问照片。" cancelString:@"取消" cancleBlock:nil sureString:@"去设置" sureBlock:^{
                // 需要在info.plist中添加 URL types 并设置一项URL Schemes为prefs  IOS10 以后不起作用 else的方法
                
                    if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]){
                    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
                }
            } currentController:[DY_Common getCurrentVC]];
        }
        
        
        return NO;
    }else if(status == ALAuthorizationStatusNotDetermined){
        
    }
    
    return  YES;
}

  • 做照相机权限校验
/**
 去设置相机权限的的时候系统会kill 当前app进程 Message from debugger: Terminated due to signal 9
 @param showAlert <#showAlert description#>
 @return <#return value description#>
 */
+(BOOL)isHasCameraAuthorityWithisShowAlert:(BOOL)showAlert
{
    AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if (status == AVAuthorizationStatusRestricted || status == AVAuthorizationStatusDenied) {
        NSLog(@"LBLog 没有访问相机的权限");
        if (showAlert) {
            [LBAlertController showAlertTitle:@"无法使用相机" content:@"请在iPhone的\"设置-隐私-相机\"中允许访问相机。" cancelString:@"取消" cancleBlock:nil sureString:@"去设置" sureBlock:^{
                // 需要在info.plist中添加 URL types 并设置一项URL Schemes为prefs  IOS10 以后不起作用
                    if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]){
                    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
                }
            } currentController:[DY_Common getCurrentVC]];
            return NO;
        }else if (status == AVAuthorizationStatusNotDetermined){
            [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
                if (granted) {
                    NSLog(@"LBLog 获取相机权限正常==============");
                    
                }else{
                    NSLog(@"LBLog 获取相机权限不正常==============");
                }
            }];
        }
    }
    NSLog(@"LBLog 有访问相机的权限 =============");
    return YES;
}

III 预备知识

3.1 Base64 转UIImage

  • Base64 转UIImage
+ (UIImage *)stringToImage:(NSString *)str {

NSData * imageData =[[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters];

UIImage *photo = [UIImage imageWithData:imageData ];

return photo;

}


3.2 将当前的VC的界面作为另一个VC的背景图片

使用系统自带的modal样式:UIModalPresentationOverCurrentContext

  • A presentation style where the content is displayed over another view controller’s content)

———————————————— 版权声明:本文为CSDN博主「iOS逆向」的原创文章,遵循CC 4.0 >BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:blog.csdn.net/z929118967/…

3.3 视频下载,并保存到相册

iOS NSFileManager类对文件的操作、NSFileHandle对文件内容进行读取和写入例子:使用dataWithContentsOfURL进行视频下载,并保存到相册

  1. 使用dataWithContentsOfURL进行视频下载,并保存到相册
  2. 从沙盒获取信息:NSFileManager类主要对文件的操作(删除、修改、移动、复制等等) NSFileHandle 类主要对文件内容进行读取和写入操作

———————————————— 版权声明:本文为CSDN博主「#公号:iOS逆向」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:blog.csdn.net/z929118967/…