引言
iOS 保存图片到相册的需求:二维码界面,新增一个“保存到相册”按钮,点击完成将核心视图信息保存到相册 。
- 监听到用户点击不允许,不允许时显示引导
- 用户未作出明确选择的情况下自己主动请求了一次权限设置(刷新UI的代码放到主线程执行)
预备知识:
- iOS小技能:iOS14相册权限适配 (Limited Photo Library Access)
- 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
2.2 监听第一次授权时,用户点击不允许和允许
思路:
- 监听到用户点击不允许,不允许时显示引导
- 用户未作出明确选择的情况下自己主动请求了一次权限设置(
刷新UI的代码放到主线程执行
)
dispatch_sync(dispatch_get_main_queue(), ^{
//刷新UI的代码放到主线程执行
用法:
YES block:^(id
代码实现:
/**
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
3.2 将当前的VC的界面作为另一个VC的背景图片
使用系统自带的modal样式:UIModalPresentationOverCurrentContext
- A presentation style where the content is displayed over another view controller’s content)
3.3 视频下载,并保存到相册
iOS NSFileManager类对文件的操作、NSFileHandle对文件内容进行读取和写入例子:使用dataWithContentsOfURL进行视频下载,并保存到相册
- 使用dataWithContentsOfURL进行视频下载,并保存到相册
- 从沙盒获取信息:NSFileManager类主要对文件的操作(删除、修改、移动、复制等等) NSFileHandle 类主要对文件内容进行读取和写入操作