Swift - 保存图片到系统相册(相机胶卷),并获取存放路径、缩略图
作者:hangge | 2016-04-13 08:30
(本文代码已升级至Swift3)
有时我们需要把图像(比如:从网络上获取的图片、程序自己生成的图片等)保存到系统相册(照片)中,通常有两种办法:一是使用 UIImageWriteToSavedPhotosAlbum() 方法保存,二是使用 Photos 框架来实现。
(照片库 ALAssetsLibrary 以后会被废弃,所以不建议继续使用。)
下面以将imageView中的图像保存到相册为例,分别演示这两种实现方式。

准备工作:Info.plist配置
由于苹果安全策略更新,在使用Xcode8开发时,需要在 Info.plist 配置请求照片相的关描述字段(Privacy - Photo Library Usage Description)
1,使用UIImageWriteToSavedPhotosAlbum()方法保存图片
UIImageWriteToSavedPhotosAlbum()优点是使用简单,一句话就搞定。但如果想要取得刚刚保存的地址就没办法了。import UIKit
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func saveImage(_ sender: AnyObject) {
let image = self.imageView.image!
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
}
可以看到图片被保存到相机胶卷中去了:
2,使用Photos框架来保存图片
在iOS 8之前,开发者只能用 AssetsLibrary 框架访问的用户的照片库。几年以来,相机应用和照片应用发生了显著的变化,增加了许多新特性,包括按时刻来组织照片的方式。但与此同时,AssetsLibrary 框架却没有跟上步伐。随着 iOS 8 的到来,苹果给我们提供了一个现代化的框架 PhotoKit,它比 AssetsLibrary 表现更好,并且拥有让应用和设备照片库无缝工作的特性。
(1)保存图片到系统相册中
import UIKit
import Photos
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func saveImage(_ sender: AnyObject) {
let image = self.imageView.image!
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAsset(from: image)
}) { (isSuccess: Bool, error: Error?) in
if isSuccess {
print("保存成功!")
} else{
print("保存失败:", error!.localizedDescription)
}
}
}
}
(2)保存成功后获取照片保存路径
在Photos框架中,官方淡化照片库中 URL 的概念,改之使用一个标志符(localIdentifier)来唯一代表一个资源。
所以我们可以在操作时保存下标志符,然后在保存成功时通过这个标志符获取保存后的图片资源(PHAsset),最后通过这个 PHAsset 来获取路径。
import UIKit
import Photos
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
//存放照片资源的标志符
var localId:String!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func saveImage(_ sender: AnyObject) {
let image = self.imageView.image!
PHPhotoLibrary.shared().performChanges({
let result = PHAssetChangeRequest.creationRequestForAsset(from: image)
let assetPlaceholder = result.placeholderForCreatedAsset
//保存标志符
self.localId = assetPlaceholder?.localIdentifier
}) { (isSuccess: Bool, error: Error?) in
if isSuccess {
print("保存成功!")
//通过标志符获取对应的资源
let assetResult = PHAsset.fetchAssets(
withLocalIdentifiers: [self.localId], options: nil)
let asset = assetResult[0]
let options = PHContentEditingInputRequestOptions()
options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData)
-> Bool in
return true
}
//获取保存的图片路径
asset.requestContentEditingInput(with: options, completionHandler: {
(contentEditingInput:PHContentEditingInput?, info: [AnyHashable : Any]) in
print("地址:",contentEditingInput!.fullSizeImageURL!)
})
} else{
print("保存失败:", error!.localizedDescription)
}
}
}
}
模拟器中运行,输出的图片存储路径如下:
真机调试,输出的图片存储路径如下:

(3)保存成功后获取照片的原图,缩略图
和上面一样,也是先要通过标志符(localIdentifier)来获得图片资源(PHAsset),然后通过 PHImageManager 的 requestImage() 方法获取这个资源的图片。
requestImage()方法的参数说明:
asset:图像对应的 PHAsset
targetSize:需要获取的图像的尺寸,如果输入的尺寸大于资源原图的尺寸,则只返回原图。(如果需要返回原图尺寸,可以传入 PhotoKit 中预先定义好的常量 PHImageManagerMaximumSize ,表示返回可选范围内的最大的尺寸,即原图尺寸。)
contentMode:图像的剪裁方式,与 UIView 的 contentMode 参数相似,控制照片应该以按比例缩放还是按比例填充的方式放到最终展示的容器内。(注意:如果 targetSize 传入 PHImageManagerMaximumSize,则 contentMode 无论传入什么值都会被视为 PHImageContentModeDefault )
options:一个 PHImageRequestOptions 的实例,可以控制的内容相当丰富,包括图像的质量、版本,也会有参数控制图像的剪裁。
resultHandler:请求结束后被调用的 block,返回一个包含资源对于图像的 UIImage 和包含图像信息的一个 NSDictionary,在整个请求的周期中,这个 block 可能会被多次调用。
asset:图像对应的 PHAsset
targetSize:需要获取的图像的尺寸,如果输入的尺寸大于资源原图的尺寸,则只返回原图。(如果需要返回原图尺寸,可以传入 PhotoKit 中预先定义好的常量 PHImageManagerMaximumSize ,表示返回可选范围内的最大的尺寸,即原图尺寸。)
contentMode:图像的剪裁方式,与 UIView 的 contentMode 参数相似,控制照片应该以按比例缩放还是按比例填充的方式放到最终展示的容器内。(注意:如果 targetSize 传入 PHImageManagerMaximumSize,则 contentMode 无论传入什么值都会被视为 PHImageContentModeDefault )
options:一个 PHImageRequestOptions 的实例,可以控制的内容相当丰富,包括图像的质量、版本,也会有参数控制图像的剪裁。
resultHandler:请求结束后被调用的 block,返回一个包含资源对于图像的 UIImage 和包含图像信息的一个 NSDictionary,在整个请求的周期中,这个 block 可能会被多次调用。
import UIKit
import Photos
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
//存放照片资源的标志符
var localId:String!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func saveImage(_ sender: AnyObject) {
let image = self.imageView.image!
PHPhotoLibrary.shared().performChanges({
let result = PHAssetChangeRequest.creationRequestForAsset(from: image)
let assetPlaceholder = result.placeholderForCreatedAsset
//保存标志符
self.localId = assetPlaceholder?.localIdentifier
}) { (isSuccess: Bool, error: Error?) in
if isSuccess {
print("保存成功!")
//通过标志符获取对应的资源
let assetResult = PHAsset.fetchAssets(
withLocalIdentifiers: [self.localId], options: nil)
let asset = assetResult[0]
let options = PHContentEditingInputRequestOptions()
options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData)
-> Bool in
return true
}
//获取保存的原图
PHImageManager.default().requestImage(for: asset,
targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit,
options: nil, resultHandler: { (image, _:[AnyHashable : Any]?) in
print("获取原图成功:\(image)")
})
//获取保存的缩略图
PHImageManager.default().requestImage(for: asset,
targetSize: CGSize(width:100, height:100), contentMode: .aspectFit,
options: nil, resultHandler: { (image, _:[AnyHashable : Any]?) in
print("获取缩略图成功:\(image)")
})
} else{
print("保存失败:", error!.localizedDescription)
}
}
}
}
控制台输出如下:

全部评论(4)
如果是保存UIImageView中的图片,可以保存成功,但是无法保存自己new出来的UIImage
站长回复:我测试了下是没问题的啊:
航哥:
从您这里学习了很多,表格,多页面跳转,控件使用,地图
还有能把这个代码升级到swift3吗?
站长回复:代码已更新,你可以再看下。
hangge能不能把这篇代码改成swift3的呢,谢谢!
站长回复:代码已更新,你可以再看下。
太赞了,hangge.等我把现在的任务完成就来学习
站长回复:加油!