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)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,176评论 5 469
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,190评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,232评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,953评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,879评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,177评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,626评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,295评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,436评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,365评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,414评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,096评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,685评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,771评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,987评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,438评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,032评论 2 341

推荐阅读更多精彩内容