Swift 4.0 Photos框架
2018-02-22 本文已影响180人
一欧Yiou
Photos简介
在iOS 8之前,开发者只能用 AssetsLibrary框架访问的用户的照片库。
几年以来,相机应用和照片应用发生了显著的变化,增加了许多新特性,包括按时刻来组织照片的方式。
但与此同时,AssetsLibrary框架却没有跟上步伐。
随着 iOS 8 的到来,苹果给我们提供了一个现代化的框架 Photos,它比 AssetsLibrary表现更好,并且拥有让应用和设备照片库无缝工作的特性。
常用类简介
几个常用的类:
PHAsset : 代表照片库中的一个资源,跟 ALAsset 类似,通过 PHAsset 可以获取和保存资源。每个PHAsset就是一张图片的详细信息,包括图片、位置、时间等。
PHFetchOptions : 获取资源时的参数,可以传 nil,即使用系统默认值。
PHFetchResult : 表示一系列的资源集合,也可以是相册的集合。
PHAssetCollection : 表示一个相册或者一个时刻,或者是一个智能相册(系统提供的特定的一系列相册,例如:最近删除,视频列表,收藏等等)。
PHImageManager : 用于处理资源的加载,加载图片的过程带有缓存处理,可以通过传入一个
PHImageRequestOptions 控制资源的输出尺寸等规格。
enum PHAssetCollectionType : Int {
case Album //从 iTunes 同步来的相册,以及用户在 Photos 中自己建立的相册
case SmartAlbum //经由相机得来的相册
case Moment //Photos 为我们自动生成的时间分组的相册
}
enum PHAssetCollectionSubtype : Int {
case AlbumRegular //用户在 Photos 中创建的相册,也就是我所谓的逻辑相册
case AlbumSyncedEvent //使用 iTunes 从 Photos 照片库或者 iPhoto 照片库同步过来的事件。然而,在iTunes 12 以及iOS 9.0 beta4上,选用该类型没法获取同步的事件相册,而必须使用AlbumSyncedAlbum。
case AlbumSyncedFaces //使用 iTunes 从 Photos 照片库或者 iPhoto 照片库同步的人物相册。
case AlbumSyncedAlbum //做了 AlbumSyncedEvent 应该做的事
case AlbumImported //从相机或是外部存储导入的相册,完全没有这方面的使用经验,没法验证。
case AlbumMyPhotoStream //用户的 iCloud 照片流
case AlbumCloudShared //用户使用 iCloud 共享的相册
case SmartAlbumGeneric //文档解释为非特殊类型的相册,主要包括从 iPhoto 同步过来的相册。由于本人的 iPhoto 已被 Photos 替代,无法验证。不过,在我的 iPad mini 上是无法获取的,而下面类型的相册,尽管没有包含照片或视频,但能够获取到。
case SmartAlbumPanoramas //相机拍摄的全景照片
case SmartAlbumVideos //相机拍摄的视频
case SmartAlbumFavorites //收藏文件夹
case SmartAlbumTimelapses //延时视频文件夹,同时也会出现在视频文件夹中
case SmartAlbumAllHidden //包含隐藏照片或视频的文件夹
case SmartAlbumRecentlyAdded //相机近期拍摄的照片或视频
case SmartAlbumBursts //连拍模式拍摄的照片,在 iPad mini 上按住快门不放就可以了,但是照片依然没有存放在这个文件夹下,而是在相机相册里。
case SmartAlbumSlomoVideos //Slomo 是 slow motion 的缩写,高速摄影慢动作解析,在该模式下,iOS 设备以120帧拍摄。不过我的 iPad mini 不支持,没法验证。
case SmartAlbumUserLibrary //这个命名最神奇了,就是相机相册,所有相机拍摄的照片或视频都会出现在该相册中,而且使用其他应用保存的照片也会出现在这里。
case Any //包含所有类型
}
使用
(1)须先在info.plist
中添加 NSPhotoLibraryUsageDescription
(2)在使用其他功能之前先验证是否有权限
let library = PHPhotoLibrary.authorizationStatus()
if (library == PHAuthorizationStatus.denied ||
library == PHAuthorizationStatus.restricted ||
library == PHAuthorizationStatus.notDetermined) {
print("没有权限")
}else {
print("有权限")
}
//判断是否授权
func isAuthorized() -> Bool {
return PHPhotoLibrary.authorizationStatus() == .authorized ||
PHPhotoLibrary.authorizationStatus() == .notDetermined
}
(3)保存图片到系统相册
//保存图片到系统相册
func saveImage() {
let image = UIImage(named:"swift.jpg")
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAsset(from: image!)
}) { (isSuccess: Bool, error: Error?) in
if isSuccess {
print("保存成功!")
} else{
print("保存失败:", error!.localizedDescription)
}
}
}
(4)保存图片到系统相册
enum PhotoAlbumUtilResult {
case success
case error
case denied
}
//保存图片到相册
func saveImageInAlbum(image: UIImage, albumName: String = "", completion: ((_ result: PhotoAlbumUtilResult) -> ())?) {
//权限验证
if !isAuthorized() {
completion?(.denied)
return
}
var assetAlbum: PHAssetCollection?
//如果没有传相册的名字,则保存到相机胶卷。(否则保存到指定相册)
if albumName.isEmpty {
print("没有传相册的名字")
}else {
//看保存的指定相册是否存在
let list = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: nil)
list.enumerateObjects({ (album, index, stop) in
let assetCollection = album
if albumName == assetCollection.localizedTitle {
assetAlbum = assetCollection
stop.initialize(to: true)
}
})
//不存在的话则创建该相册
if assetAlbum == nil {
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest
.creationRequestForAssetCollection(withTitle: albumName)
}, completionHandler: { (isSuccess, error) in
self.saveImageInAlbum(image: image, albumName: albumName,
completion: completion)
})
return
}
}
//保存图片
PHPhotoLibrary.shared().performChanges({
//添加的相机胶卷
let result = PHAssetChangeRequest.creationRequestForAsset(from: image)
//是否要添加到相簿
if !albumName.isEmpty {
let assetPlaceholder = result.placeholderForCreatedAsset
let albumChangeRequset = PHAssetCollectionChangeRequest(for:
assetAlbum!)
albumChangeRequset!.addAssets([assetPlaceholder!] as NSArray)
}
}) { (isSuccess: Bool, error: Error?) in
if isSuccess {
completion?(.success)
} else{
print(error!.localizedDescription)
completion?(.error)
}
}
}
//方法调用
saveImageInAlbum(image: UIImage(named:"swift.jpg")!, albumName: "MyPhoto") { (result) in
switch result{
case .success:
print("保存成功")
case .denied:
print("被拒绝")
case .error:
print("保存错误")
}
}
(5)保存成功后获取照片保存路径
在Photos框架中,官方淡化照片库中 URL 的概念,改之使用一个标志符(localIdentifier)来唯一代表一个资源。
所以我们可以在操作时保存下标志符,然后在保存成功时通过这个标志符获取保存后的图片资源(PHAsset),最后通过这个 PHAsset 来获取路径。
//存放照片资源的标志符
var localId: String!
func saveImage() {
let image = UIImage(named:"swift.jpg")
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)
}
}
}
(6)保存成功后获取照片的原图,缩略图
和上面一样,也是先要通过标志符(localIdentifier)来获得图片资源(PHAsset),然后通过 PHImageManager 的 requestImage() 方法获取这个资源的图片。
requestImage()方法的参数说明:
asset:图像对应的 PHAsset
targetSize:需要获取的图像的尺寸,如果输入的尺寸大于资源原图的尺寸,则只返回原图。(如果需要返回原图尺寸,可以传入 PhotoKit 中预先定义好的常量 PHImageManagerMaximumSize ,表示返回可选范围内的最大的尺寸,即原图尺寸。)
contentMode:图像的剪裁方式,与 UIView 的 contentMode 参数相似,控制照片应该以按比例缩放还是按比例填充的方式放到最终展示的容器内。(注意:如果 targetSize 传入 PHImageManagerMaximumSize,则 contentMode 无论传入什么值都会被视为 PHImageContentModeDefault )
options:一个 PHImageRequestOptions 的实例,可以控制的内容相当丰富,包括图像的质量、版本,也会有参数控制图像的剪裁。
resultHandler:请求结束后被调用的 block,返回一个包含资源对于图像的 UIImage 和包含图像信息的一个 NSDictionary,在整个请求的周期中,这个 block 可能会被多次调用。
func saveImage() {
let image = UIImage(named:"swift.jpg")
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
//image
print("获取原图成功")
})
//获取保存的缩略图
PHImageManager.default().requestImage(for: asset,
targetSize: CGSize(width:100, height:100), contentMode: .aspectFit,
options: nil, resultHandler: { (image, _:[AnyHashable : Any]?) in
//image
print("获取缩略图成功")
})
} else{
print("保存失败:", error!.localizedDescription)
}
}
}