当前位置: 首页 > 编程日记 > 正文

iOS 相册和网络图片的存取

iOS 相册和网络图片的存取

保存 UIImage 到相册

UIKit

UIKit 中一个古老的方法,Objective-C 的形式

void UIImageWriteToSavedPhotosAlbum(UIImage *image, id completionTarget, SEL completionSelector, void *contextInfo);

保存完成后,会调用 completionTarget 的 completionSelector。如果 completionTarget 不为空,completionTarget 必须实现以下方法

- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo;

Objective-C 的写法

- (void)saveImage:(UIImage *)image {UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); 
}- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {if (error) {// Fail} else {// Success}
}

Swift 的写法

func saveImage(_ image: UIImage) {UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}func image(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: AnyObject) {if error == nil {// Success} else {// Fail}
}

Photos framework

iOS 8 开始,可以用 Photos framework。PHAssetChangeRequest 的类方法可以保存 UIImage

class func creationRequestForAsset(from image: UIImage) -> Self

编辑相册需要在 PHPhotoLibrary 的闭包中进行,有两种方法

func performChanges(_ changeBlock: @escaping () -> Void, completionHandler: ((Bool, Error?) -> Void)? = nil)
func performChangesAndWait(_ changeBlock: @escaping () -> Void) throws

以上两种方法,分别是异步和同步执行。一般用第一种异步执行的方法,不会阻塞主线程。

func saveImage(_ image: UIImage) {PHPhotoLibrary.shared().performChanges({ PHAssetChangeRequest.creationRequestForAsset(from: image)}, completionHandler: { (success, error) in// NOT on main threadif success {// Success} else if let error = error {// Handle error}})
}

编辑相册的闭包 changeBlock 和完成的闭包 completionHandler,是在 serial queue 中执行,不在主线程。需要更新 UI 的话,要切换到主线程中执行。

保存图片的 Data 到相册

如果有图片的数据(Data 或 NSData),可以用 Photos framework 的方法保存到相册。从 iOS 9 开始,可以使用 PHAssetCreationRequest 的方法

func addResource(with type: PHAssetResourceType, data: Data, options: PHAssetResourceCreationOptions?)

iOS 8 比较麻烦,需要把数据写入临时文件,用临时文件的 URL 作为参数,调用 PHAssetChangeRequest 的类方法

class func creationRequestForAssetFromImage(atFileURL fileURL: URL) -> Self?

以下是兼容 iOS 8 的写法

func saveImageData(_ data: Data) {if #available(iOS 9.0, *) {PHPhotoLibrary.shared().performChanges({PHAssetCreationRequest.forAsset().addResource(with: .photo, data: data, options: nil)}, completionHandler: { (success, error) in// NOT on main threadif success {// Success} else if let error = error {// Handle error}})} else {// Write image data to temp filelet tempPath = NSTemporaryDirectory().appending("TempImageToSaveToPhoto.image")let tempUrl = URL(fileURLWithPath: tempPath)try? data.write(to: tempUrl)PHPhotoLibrary.shared().performChanges({PHAssetChangeRequest.creationRequestForAssetFromImage(atFileURL: tempUrl)}, completionHandler: { (success, error) in// NOT on main threadif success {// Success} else if let error = error {// Handle error}// Remove temp filetry? FileManager.default.removeItem(at: tempUrl)})}
}

SDWebImage 缓存 UIImage、Data

SDWebImage (目前版本 4.0.0) 有两个方法可以使用。

SDWebImageManager 的方法

- (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url;

SDImageCache 的方法

- (void)storeImage:(nullable UIImage *)imageimageData:(nullable NSData *)imageDataforKey:(nullable NSString *)keytoDisk:(BOOL)toDiskcompletion:(nullable SDWebImageNoParamsBlock)completionBlock;

1089786-20170425194117631-1601821212.png

这个方法的 image、key 参数不能为空,否则直接执行 completionBlock 就返回。

从相册获取 UIImage、Data

UIImagePickerController 是常用的照片选取控制器。实现一个代理方法即可

optional func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])

通过 info 字典,可以获取 UIImage 等信息。这里用来查询 info 字典的 key 有

UIImagePickerControllerOriginalImage // 原始 UIImage
UIImagePickerControllerEditedImage // 编辑后的 UIImage
UIImagePickerControllerReferenceURL // ALAsset 的 URL

通过 ALAsset 的 URL 可获取 PHAsset。通过 PHImageManager 的方法可以获得相册图片的 Data

func requestImageData(for asset: PHAsset, options: PHImageRequestOptions?, resultHandler: @escaping (Data?, String?, UIImageOrientation, [AnyHashable : Any]?) -> Void) -> PHImageRequestID

以下是代码示例

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {picker.dismiss(animated: true, completion: nil)if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {// Get original image}if let url = info[UIImagePickerControllerReferenceURL] as? URL,let asset = PHAsset.fetchAssets(withALAssetURLs: [url], options: nil).firstObject {PHImageManager.default().requestImageData(for: asset, options: nil, resultHandler: { (imageData, _, _, _) inif let data = imageData {// Get image data}})}
}

从 SDWebImage 的缓存中获取 UIImage、Data

SDWebImage 给 UIImageView 提供了方法,方便获取、显示网络图片。如果需要获取下载的图片(进行保存到相册、上传至服务器等操作),可以用以下方法

- (nullable id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)urloptions:(SDWebImageOptions)optionsprogress:(nullable SDWebImageDownloaderProgressBlock)progressBlockcompleted:(nullable SDInternalCompletionBlock)completedBlock;

Swift 的代码示例

SDWebImageManager.shared().loadImage(with: url, options: SDWebImageOptions(rawValue: 0), progress: nil, completed: { [weak self] (cachedImage, imageData, error, _, _, _) inguard self != nil else { return }if let image = cachedImage {// Get image}if let data = imageData {// Get image data}if error != nil {// Handle error}
})

这个方法有个问题,对于静态图片,可能获取不到 Data。如果需要获取图片 Data 的话,不能直接这么写。查看源码可以找到原因。SDWebImageManager 的 loadImage: 方法会调用 SDImageCache 的 queryCacheOperationForKey: 方法

1089786-20170425194137131-785031456.png

diskImageDataBySearchingAllPathsForKey: 方法用来获取 Disk 中图片的 Data。当图片在 Memory 中,只有 GIF 图片才会提供 Data,静态图的 Data 为空;当图片在 Disk 中,都会提供 Data。如果能在外部直接调用 diskImageDataBySearchingAllPathsForKey: 方法就很简单,但是不行,这是私有方法,只写在 .m 文件里,对外不可见。

改源码可以解决问题,将上图第一个箭头的 if 判断去掉,总是调用 diskImageDataBySearchingAllPathsForKey: 方法。然而,改第三方库源码不好,可能会有想不到的糟糕后果。

一种方法是,根据 diskImageExistsWithKey: 方法,获取 Disk 上的 Data。

1089786-20170425194151787-1809376809.png

判断 Disk 的图片是否存在,就是查找两个路径。同样,拿到这两个路径的文件就可以获得 Data。以下是 Swift 代码示例

SDWebImageManager.shared().diskImageExists(for: imageUrl) { [weak self] (exist) in// Always on main threadguard self != nil else { return }if exist {// Find image data from diskvar data: NSData?// Get cache keylet key = SDWebImageManager.shared().cacheKey(for: imageUrl)// Get cache pathif let path = SDImageCache.shared().defaultCachePath(forKey: key) {data = NSData(contentsOfFile: path)if data == nil {data = NSData(contentsOfFile: (path as NSString).deletingPathExtension)}}if data != nil {// Get image data} else {// Fail getting image data}} else {// No disk image}
}

这个方法缺点在于,代码复杂,可能会在 SDWebImage 版本升级后失效(例如,Disk 缓存路径改变)。

推荐的方法是,将图片缓存从 Memory 中移除,然后调用 SDWebImageManager 的 loadImage: 方法。

// Get cache key
let key = SDWebImageManager.shared().cacheKey(for: imageUrl)
// Remove memory cache
SDImageCache.shared().removeImage(forKey: key, fromDisk: false, withCompletion: nil)
// Load image and data
SDWebImageManager.shared().loadImage(with: imageUrl, options: SDWebImageOptions(rawValue: 0), progress: nil) { [weak self] (_, data, _, _, _, _) inguard self != nil else { return }if data != nil {// Get image data} else {// Fail getting image data}
}

这样写比较简洁。即使 SDWebImage 版本升级后改变 Disk 缓存路径,依然有效。以上代码执行之后,当前图片又会存在 Memory 中。

未解决的问题

将 JPG 图片的 Data 保存至相册,然后再取出的 Data 与保存的 Data 可能不一样。requestImageData: 方法传入 PHImageRequestOptions,PHImageRequestOptions 的 version 试了三种值(current、unadjusted、original)都不行。PNG、GIF 图片还没遇到这个问题。可能保存 JPG 图片的过程会修改原始数据。如何使存取的数据一致?欢迎交流!

转载请注明出处:http://www.cnblogs.com/silence-cnblogs/p/6763928.html

转载于:https://www.cnblogs.com/silence-cnblogs/p/6763928.html

相关文章:

微信小程序实时聊天之WebSocket

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 正文&#xff1a; 1.所有监听事件先在onload监听。 // pages/index/to_news/to_news.js var app getApp(); var socketOpen false; var SocketTask false; var url ws://192.168.0.120:7011; Page…

webform repeater

repeater:由模板构成&#xff0c;解析后模板就不存在了 需要指定数据源进行数据绑定 List<Fruit> list new FruitDA().Select(); // 数据查询 &#xff08;随便查寻的&#xff09; Repeater1.DataSource list; // 赋值 Repeater1…

远程协助软件开发_这是我从事远程软件开发人员工作的主要技巧

远程协助软件开发by Colin Morgan通过科林摩根(Colin Morgan) 这是我从事远程软件开发人员工作的主要技巧 (Here are the top tips I’ve used to land a remote software developer job) Applying for a remote software developer job means you are voluntarily choosing t…

简谈-Python一些常用的爬虫技巧

第一种&#xff1a;基本的网页抓取 get方法 import urllib2url "链接response urllib2.urlopen(url)print response.read() post方法 import urllibimport urllib2url "链接form {name:abc,password:1234}form_data urllib.urlencode(form)request urllib2.Req…

微信小程序画布圆形进度条demo

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 正文&#xff1a; wxml <!--pages/test/test.wxml--> <canvas style"width: 300px; height: 200px;" canvas-id"canvasid"></canvas>js // pages/test/test.js …

smarty 模板引擎

http://blog.csdn.net/zuiaituantuan/article/details/5951242 http://wenku.baidu.com/link?url-UHlSnTXOOAjFG1KjX6T9sEG6V4hNAMfRDpMuRRnc_FKbFAxiE5Ntk4lzxSm-7Z531uWdfvgYx81sdC61SgTZm7q8FdUt3gSs7ZlC0JR1SW转载于:https://www.cnblogs.com/hxjbc/p/4441879.html

flask url构建_如何为生产构建构建Flask-RESTPlus Web服务

flask url构建by Greg Obinna由格雷格奥比纳(Greg Obinna) 如何为生产构建构建Flask-RESTPlus Web服务 (How to structure a Flask-RESTPlus web service for production builds) In this guide I’ll show you a step by step approach for structuring a Flask RESTPlus web…

【2017-4-26】Winform 公共控件 菜单和工具栏

作废 等待重写 名称 功能取值赋值备注Button按钮多用来触发点击事件 CheckBox多选按钮 CheckedListBox多选按钮组 ComboBox下拉列表 DateTimePicker指定的格式选择时间日期 Lable说明性文字控件 LinkLable超链接类型文件控件 ListBox用户选择项 ListVie…

微信小程序限制当前位置和目的地的距离

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 正文&#xff1a; 1。获取当前位置经纬度 onLoad: function (options) {var that this;campaign_id campaign_idwx.getLocation({type: wgs84,success: function (res) {console.log(res)lat1 res.l…

命令行的全文搜索工具--ack

想必大家在命令行环境下工作时候&#xff0c;一定有想要查找当前目录下的源代码文件中的某些字符的需求&#xff0c;这时候如果使用传统方案&#xff0c;你可能需要输入一长串的命令&#xff0c;比如这样&#xff1a; 1. grep -R string dir/ 或者 grep -r -e string direct…

ecmascript_TC39及其对ECMAScript的贡献

ecmascriptby Parth Shandilya通过Parth Shandilya TC39及其对ECMAScript的贡献 (TC39 and its contributions to ECMAScript) Many people get confused about what is JavaScript and what is ECMAScript. Sometimes it’s hard to tell how they are connected with each o…

Winio驱动在64位windows下无法使用的解决方法

C#在使用WinIo的驱动开发类似按键精灵一类工具的时候&#xff0c;需要对相关的驱动进行注册才能正常启动&#xff0c;找了下资料&#xff0c;资料来自&#xff1a; http://jingyan.baidu.com/article/642c9d34e55bd9644b46f74e.html 我在这里进行转载&#xff1a; Winio驱动在6…

js获取前后几天或者前后几个月的日期

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 &#xff1b; 正文&#xff1a; demo: 1.获取前后几天的日期 // pages/test/test.jsPage({onLoad: function (options) {var day -7;console.log(GetDay(day))}, }) function GetDay(day) {var tim…

nodejs安装、配置及开发工具

学了node一段时间&#xff0c;但是node的安装还是有一点迷糊。今天新换电脑&#xff0c;所以&#xff0c;需要从头开始&#xff0c;发现node的安装还是不顺畅&#xff0c;这篇随笔是之前学的时候写&#xff0c;但是今天再打开看的时候&#xff0c;发现其他好像没有什么内容&…

拨测工具_您可以拨多少钱? 快速简单地介绍有用的工具。

拨测工具by Miguel Bustamante通过Miguel Bustamante 您可以卷曲多少&#xff1f; 快速简单地介绍有用的工具。 (How much can you cURL? A quick and easy intro to a useful tool.) On a good day I can flex a 20 lb weight…twice. Probably. But that’s not the type o…

leetcode第一刷_Recover Binary Search Tree

这是一道好题&#xff0c;思路尽管有&#xff0c;可是提交之后总是有数据过不了&#xff0c;又依照数据改改改。最后代码都没法看了。收到的教训是假设必须为自己的代码加上非常多非常多特殊的限定。来过一些特殊的数据的话。说明代码本身有非常大的漏洞。 这道题&#xff0c;我…

Java中的文件路径

通常情况下&#xff0c;在Java项目中&#xff0c;我们使用的路径都是在拿到类加载路径后&#xff0c;根据相对位置&#xff0c;使用 FilePathTest.class.getResourceAsStream(relativePath)&#xff1b;拿到文件。今天小生不使用classPath&#xff0c;而是直接去使用相对路径来…

js上传文件,上传表单demo 包含后端php

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 正文&#xff1a; <!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><title>Title</title><script src"https://ajax.as…

如何在Tensorflow.js中处理MNIST图像数据

by Kevin Scott凯文斯科特(Kevin Scott) 如何在Tensorflow.js中处理MNIST图像数据 (How to deal with MNIST image data in Tensorflow.js) There’s the joke that 80 percent of data science is cleaning the data and 20 percent is complaining about cleaning the data …

常用图像额文件格式及类型

1、显示一幅二值图像&#xff1a; >> bw zeros(90,90); >> bw(2:2:88,2:2:88) 1; >> imshow(bw); >> 2、利用image函数显示一幅索引图像&#xff1a; >> [X,MAP] imread(E:\STUDY_software\Matlab2016\images\11.jpg); >> image(X); &…

微信小程序实现滑动翻页效果源码附效果图

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 正文&#xff1a; 微信小程序实现滑动翻页效果 效果图&#xff1a; 源码&#xff1a; <view class"mainFrame"><swiper class"container" indicator-dots"{{indic…

Ubuntu 系统 文件操作命令

文件和目录的操作 用户主目录下有一个 Desktop (对应,桌面)mkdir dir1 建立一个目录cd 不添加参数,默认回到主目录(用户目录)touch a.txt 建立一个文件mv a.txt Desktop/ 移动到Desktop/中 mkdir dir1cp -r dir1/ dir2 不加-r或者&#xff0d;R的时候&#xff0c;只拷贝文件&am…

firebase 推送_如何使用Firebase向Web应用程序添加推送通知?

firebase 推送by Leonardo Cardoso由莱昂纳多卡多佐(Leonardo Cardoso) 如何使用Firebase向Web应用程序添加推送通知&#xff1f; (How to add push notifications to a web app with Firebase ??) As web applications evolve, it is increasingly common to come across f…

lucene构建同义词分词器

lucene4.0版本号以后 已经用TokenStreamComponents 代替了TokenStream流。里面包含了filter和tokenizer 在较复杂的lucene搜索业务场景下&#xff0c;直接网上下载一个作为项目的分词器&#xff0c;是不够的。那么怎么去评定一个中文分词器的好与差&#xff1a;一般来讲。有两个…

正则匹配出字符串中两串固定字符区间的所有字符

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 正文&#xff1a; 效果&#xff1a;匹配两个字符串区间的字符串 代码&#xff1a; var dd[];var str is_img"https://www.baidu.com/"is_img11is_img"https://www.baidu.com/"is…

识别手写字体app_我如何构建手写识别器并将其运送到App Store

识别手写字体app从构建卷积神经网络到将OCR部署到iOS (From constructing a Convolutional Neural Network to deploying an OCR to iOS) 项目动机✍️?? (The Motivation for the Project ✍️ ??) While I was learning how to create deep learning models for the MNIS…

20155307 2016-2017-2 《Java程序设计》第10周学习总结

20155307 2016-2017-2 《Java程序设计》第10周学习总结 教材学习内容总结 网络编程&#xff1a;就是在两个或两个以上的设备(例如计算机)之间传输数据。程序员所作的事情就是把数据发送到指定的位置&#xff0c;或者接收到指定的数据。在发送和接收数据时&#xff0c;大部分的程…

WinForm 实现验证码

private void CheckIdentifyingCode() { Random r new Random(); string str ""; for (int i 0; i < 5; i) { int a r.Next(0, 10); str a;//将数字连接到一块 } Bitmap bm new Bitmap(150, 90);//创建位图对象 Graphics g Graphics.FromImage(bm);//在bm中…

微信小程序打开预览下载的文件

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 使用 wx.openDocument(obj) 方法预览 wx.downloadFile({url: http://example.com/somefile.pdf,success: function (res) {var filePath res.tempFilePathwx.openDocument({filePath: filePath,success…

aws lambda_为什么我会自动删除所有旧的推文以及我用来执行此操作的AWS Lambda函数...

aws lambdaFrom now on, my tweets are ephemeral. Here’s why I’m deleting all my old tweets, and the AWS Lambda function I’m using to do all this for free.从现在开始&#xff0c;我的推文只是短暂的。 这就是为什么我删除所有旧的推文&#xff0c;以及免费使用所有…