Photos存储、获取、更改照片详解

前言:

相册保存到系统相册中,通常有三种办法:

  • UIImageWriteToSavedPhotosAlbum() 方法保存
  • 是使用 Photos 框架来实现。
  • ALAssetsLibrary 在iOS9.0之后就被标记为过时方法,苹果建议使用Photos框架代替
问:UIImageWriteToSavedPhotosAlbum()保存图片很简单,但为什么还要用Photos?

答: 1、Photos可以为相册相片做标识,方便保存后取出它们 2、Photos有同步操作,可以同时保存多张图片 3、可以存储到特定的相册 ···(有其他优点,朋友们可以拍砖评论) Photos框架功能十分强大,不止保存功能

下面详解Photos这个iOS8.0才出现的新框架: 对 PhotoKit 基本构成的介绍:(本文采取最新的swift版本,OC得慢慢过渡到swift了)

PHAsset: 代表照片库中的一个资源,跟 ALAsset 类似,通过 PHAsset 可以获取和保存资源 PHFetchOptions: 获取资源时的参数,可以传 nil,即使用系统默认值 PHAssetCollection: PHCollection 的子类,表示一个相册或者一个时刻,或者是一个「智能相册(系统提供的特定的一系列相册,例如:最近删除,视频列表,收藏等等,如下图所示) PHFetchResult: 表示一系列的资源结果集合,也可以是相册的集合,从PHCollection 的类方法中获得 PHImageManager:用于处理资源的加载,加载图片的过程带有缓存处理,可以通过传入一个 PHImageRequestOptions 控制资源的输出尺寸等规格 PHImageRequestOptions:如上面所说,控制加载图片时的一系列参数 PHPhotoLibrary:表示由照片应用程序管理的整套资源和集合,包括存储在本地设备上和(允许情况下)存储在iCloud照片中的资源。您可以使用此对象对照片库中的对象集执行更改,例如,编辑资源元数据或内容,插入新资源或重新排列集合的成员,您还可以使用照片库对象来注册照片在内容或资源元数据和集合发生变化时发送的消息,并验证用户是否已授权您的应用访问照片内容(对PHPhotoLibrary的描述在文章末尾)

一、保存照片

1、UIImageWriteToSavedPhotosAlbum()保存照片
    let image = self.imageView.image!
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
2、Photos保存照片

保存照片到相册

    func savePhoto(image: UIImage) {
        PHPhotoLibrary.shared().performChanges({
            
            // Request creating an asset from the image.
            let creationRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)

            let assetPlaceholder = creationRequest.placeholderForCreatedAsset
            //保存标志符
            self.localId = assetPlaceholder?.localIdentifier
            
        }, completionHandler: { success, error in
            if !success { print("error creating asset: \(error)") }
            else {
                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!)
                })
            }
        })
    }

保存照片到特定相册

    func savePhoto(image: UIImage, album: PHAssetCollection) {
        PHPhotoLibrary.shared().performChanges({
                        
            // Request creating an asset from the image.
            let creationRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
            //            // Request editing the album.
            guard let addAssetRequest = PHAssetCollectionChangeRequest(for: album) else { return }
            let assetPlaceholder = creationRequest.placeholderForCreatedAsset
            //保存标志符
            self.localId = assetPlaceholder?.localIdentifier
            
            // Get a placeholder for the new asset and add it to the album editing request.
            addAssetRequest.addAssets([creationRequest.placeholderForCreatedAsset!] as NSArray)
        }, completionHandler: { success, error in
            if !success
            {
                print("error creating asset: \(error)")
            
            }
            else {
                print("添加到自定义相册成功了")
                
            }

        })
    }

二、创建自定义相册

func createAssetCollection() -> Void{

    PHPhotoLibrary.shared().performChanges({ 
            PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: "我是韦德")
    }) { (isSuccess: Bool, error) in
        if !isSuccess { print("error creating assetCollection: \(error)") }
        else {print("成功了")}
        //use the PHObjectPlaceholder object provided by the change request. After the change block completes, use the placeholder object’s localIdentifier property to fetch the created object.
    }
}

note:可以使用PHObjectPlaceholder为相册坐标识,然后在改变完成后(change block completes),获取刚才创建的相册

三、获取相册

从PHAssetCollection 获取中获取到的可以是相册也可以是资源,但无论是哪种内容,都统一使PHFetchResult 对象封装起来,因此虽然 PHAssetCollection 获取到的结果可能是多样的,但通过PHFetchResult 就可以使用统一的方法去处理这些内容(即遍历 PHFetchResult)

1、列出所有相册智能相册

    func getAlbum() {

        let smartAlbums: PHFetchResult = PHAssetCollection.fetchAssetCollections(with: PHAssetCollectionType.smartAlbum, subtype: PHAssetCollectionSubtype.albumRegular, options: nil)
        print("智能\(smartAlbums.count)个")
        // 这时 smartAlbums 中保存的应该是各个智能相册对应的 PHAssetCollection
        for index in 0..<smartAlbums.count {
            //获取一个相册(PHAssetCollection)
            let collection = smartAlbums[index]
            if collection.isKind(of: PHAssetCollection.classForCoder()) {
                //赋值
                let assetCollection = collection
                
                //从每一个智能相册获取到的PHFetchResult中包含的才是真正的资源(PHAsset)
                let assetsFetchResults:PHFetchResult = PHAsset.fetchAssets(in: assetCollection, options: nil)
                
                print("\(assetCollection.localizedTitle)相册,共有照片数:\(assetsFetchResults.count)")
                
                assetsFetchResults.enumerateObjects({ (asset, i, nil) in
                    //获取每一个资源(PHAsset)
                    print("\(asset)")
                })
            }
        }
    }

2、列出用户创建的相册,并获取每一个相册中的PHAsset对象

    func fetchAllUserCreatedAlbum() {
        
      //获取自定义的相册
        let topLevelUserCollections:PHFetchResult = PHCollectionList.fetchTopLevelUserCollections(with: nil)
        //topLevelUserCollections中保存的是各个用户创建的相册对应的PHAssetCollection
        print("用户创建\(topLevelUserCollections.count)个")
        
        for i in 0..<topLevelUserCollections.count {
            //获取一个相册
            let collection = topLevelUserCollections[i]
            if collection.isKind(of: PHAssetCollection.classForCoder()) {
                //赋值
                let assetCollection = collection
                
                //从每一个智能相册中获取到的PHFetchResult中包含的才是真正的资源(PHAsset)
                let assetsFetchResults:PHFetchResult = PHAsset.fetchAssets(in: assetCollection as! PHAssetCollection, options: nil)
                
                print("\(assetCollection.localizedTitle)相册,共有照片数:\(assetsFetchResults.count)")

                //遍历自定义相册,存储相片在自定义相册
                if assetCollection.localizedTitle == "HiWade" {
                    self.savePhoto(image: self.imageView.image!, album: assetCollection as! PHAssetCollection)
                }

                assetsFetchResults.enumerateObjects({ (asset, i, nil) in
                    print("\(asset)")
                })
            }
        }
        
        print("所有资源的集合,按时间排序:\(self .getAllSourceCollection())")
        

  //获取momentAlbum
        let momentAlbum:PHFetchResult = PHAssetCollection.fetchAssetCollections(with: .moment, subtype: .albumRegular, options: nil)
        print("momentAlbum:\(momentAlbum.count)个")
        
        for i in 0..<momentAlbum.count {
            let collection = momentAlbum[i]
            if collection.isKind(of: PHAssetCollection.classForCoder()) {
                //赋值
                let assetCollection = collection
                
                //从每一个智能相册中获取到的PHFetchResult中包含的才是真正的资源(PHAsset)
                let assetsFetchResults:PHFetchResult = PHAsset.fetchAssets(in: assetCollection , options: nil)
                
                print("\(assetCollection.localizedTitle)相册,共有照片数:\(assetsFetchResults.count)")
                
                assetsFetchResults.enumerateObjects({ (asset, i, nil) in
                    print("\(asset)")
                })

            }else {
                assert(false, "error")
            }
            
        }
    }

3、获取所有资源的集合,并按资源的创建时间排序

    func getAllSourceCollection() -> Array<PHAsset>{
        let options:PHFetchOptions = PHFetchOptions.init()
        var assetArr = [PHAsset]()
        options.sortDescriptors = [NSSortDescriptor.init(key: "creationDate", ascending: true)]
        let assetFetchResults:PHFetchResult = PHAsset.fetchAssets(with: options)
        for i in 0..<assetFetchResults.count {
            //获取一个资源(PHAsset)
            let asset = assetFetchResults[i]
//                self.getAssetOrigin(asset: asset)
                self.getAssetThumbnail(asset: assetFetchResults[assetFetchResults.count-1])
            

            //添加到数组
            assetArr.append(asset)
        }
        return assetArr
    }

4、获取缩略图方法

    func getAssetThumbnail(asset:PHAsset) -> Void {
        
        //获取缩略图
        let manager = PHImageManager.default()
        let option = PHImageRequestOptions() //可以设置图像的质量、版本、也会有参数控制图像的裁剪
        //返回一个单一结果,返回前会堵塞线程,默认是false
        option.isSynchronous = true
        
        manager.requestImage(for: asset, targetSize: CGSize.init(width: 100, height: 200), contentMode: .aspectFit, options: option) { (thumbnailImage, info) in
            print("缩略图:\(thumbnailImage),图像信息:\(info)")
            self.imageView.image = thumbnailImage;
            }
        }

5、获取原图的方法

    func getAssetOrigin(asset:PHAsset) -> Void { 
        //获取原图
        let manager = PHImageManager.default()
        let option = PHImageRequestOptions() //可以设置图像的质量、版本、也会有参数控制图像的裁剪
        option.isSynchronous = true
        manager.requestImage(for: asset, targetSize:PHImageManagerMaximumSize, contentMode: .aspectFit, options: option) { (originImage, info) in
            self.imageView.image = originImage;
            print("原图:\(originImage),图像信息:\(info)")
        }
    }

PHPhotoLibrary扩展:

class func authorizationStatus()```
返回是否可以进入相册的授权信息
Returns information about your app’s authorization for accessing the user’s Photos library.

 将```NSPhotoLibraryUsageDescription``` key 加入Info.plist

如果用户不允许,则会返回```not​Determined```,从而可以调用```request​Authorization(_:​)```

class func requestAuthorization((PHAuthorizationStatus) -> Void)``` 请求用户的权限,用于访问照片库。

class func shared()```
获取共享照片库对象。

func performChanges(() -> Void, completionHandler: ((Bool, Error?) -> Void)? = nil)``` 异步修改照片库

func performChangesAndWait(() -> Void)```
同步修改照片库

func register(PHPhotoLibraryChangeObserver)``` 注册一个对象来监听照片库是否改变 Registers an object to receive messages when objects in the photo library change.

func unregisterChangeObserver(PHPhotoLibraryChangeObserver)```
移除注册,不再接收改变消息
Unregisters an object so that it no longer receives change messages.




本文部分参考[API Reference](https://developer.apple.com/reference/photos)
[代码链接](https://github.com/DWadeIsTheBest/PhotosDemo)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏韩东吉的Unity杂货铺

零基础入门 20: UGUI DropDown

(题外话,因为这期分享中段制作之后,微信平台抽疯,Gif图无法使用,导致我不得不尝试用其他的方式来让文章看起来没那么死板,在后面的部分展示中,我插入了视频文件而...

3304
来自专栏nimomeng的自我进阶

Event官方文档

当系统传递一个touch event,首先会send到一个特定的view。对于touch view来讲,这个view就是被hitTest:withEve...

1232
来自专栏听雨堂

从MapX到MapXtreme2004[2]-图层操作

Mapx中基本的图层操作还是比较简单的,集中在对Layers和Layer的处理上,对别的没有太多要求。   在MapXtreme中,要完成类似功能,发生了一点...

2258
来自专栏hbbliyong

Android studio删除工程项目

 本新手最近学Android都是用的eclipse。其实个人觉得eclipse不错,可能接触Android不久,倒也不觉得它慢还是怎样。对于Google的And...

3848
来自专栏iOSDevLog

更多关于CocoaScript目录

3876
来自专栏哲学驱动设计

OEA 中 WPF 树型表格整体重构

为什么要重构     上两个月主要做了一件事情,那就是把 OEA 框架中的 TreeGrid 控件,从结构上重新设计,并大量重构现有代码。而花较大精力做这件事的...

2156
来自专栏菩提树下的杨过

silverlight:RadMaskedTextBox设置MaskType="Numeric"及Mask="n"时的一个bug

telerik的控件总体来说质量还算上乘,但是偶尔也会遇一些小bug: 比如 <telerik:RadMaskedTextBox Mask="n" MaskTy...

2279
来自专栏刘望舒

Android应用优化之流畅度实操

3113
来自专栏CRPER折腾记

Angular 2 + 折腾记 :(6) 动手实现只有年月的小组件

这个组件实现并不是很复杂,我会尽量注释; 这货诞生的理由就是项目刚好有一个地方必须只能选择年月,而github上ng2+日期组件都涉及到年月日或时分秒; 效果用...

961
来自专栏守候书阁

vue快速入门的三个小实例

用vue做项目也有一段时间了,之前也是写过关于vue和webpack构建项目的相关文章,大家有兴趣可以去看下webpack+vue项目实战(一,搭建运行环境和相...

1321

扫码关注云+社区

领取腾讯云代金券