Swift 4.0 Photos框架

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

推荐阅读更多精彩内容