iOS 相册的使用

常用类介绍

  1. PHPhotoLibrary:该类用于表示设备和iCloud上所有的收藏和资源。可以使用一个共享实例以一种线程安全的方法对照片库的变化进行管理,比如添加新的资源和相簿,或者编辑和删除已有的资源或相簿,此外,共享实例还可以注册一个关于照片库发生变化的监听对象,以实现用户界面的同步。

  2. PHAssetCollection:该类一般用于表示一组照片或者视频》可以在设备上本地创建,可以从iPhone中同步照片,可以从相册或保存照片的相簿中获取图片,或者从根据一定的约束条件得到的智能相簿中(比如全景照片)同步图片。该类还提供以组的方式访问资源。资源集合可以以集合列表的方式进行组合。

  3. PHAsset:该类用于表示照片或者视频的元数据。它提供了一些类方法,返回使用了相同约束条件的资源结果,提供带资源信息的实例方法,比如日期,位置,类型,和方向之类的信息。

  4. PHFetchResult:这是一个轻量级的对象,用于表示一组资源或者资源集合。当按照约束条件请求资源或者资源集合时m,类方法将会返回一个抓取结构(fetch result)。抓取结果会按照需求载入资源 而不是一次将所有资源载入内存,这样即使面对大资源也能很好的加以处理,抓取结果同时还是线程安全的,这意味着如果底层数据发生变化,对象的个数不会变。可以为照片库的变化注册一个请求通知类,这样变化通过PHFetchResultChangeDetails 实例发送,可以用于更新抓取结果和所有的相应的用户界面

  5. PHImageManager:图片管理器以异步的方式处理图片数据的抓取和保存,尤其适用于获取指定尺寸的图片或者管理从iCloud获取的图片数据。此外,当需要在表现图或者集合视图中显示大量资源时m,照片库还提供PHCachingImageManager 类来提升视图滑动的流畅度。

PHAssetCollectionType 和PHAssetCollectionSubtype 介绍

public enum PHAssetCollectionType : Int {
 case album       /// 自己创建的相册
 
 case smartAlbum  /// 经由系统相机得来的相册
 
 case moment      /// 自动生成的时间分组的相册
 }
 
 
 public enum PHAssetCollectionSubtype : Int {
 
 
 // PHAssetCollectionTypeAlbum regular subtypes
 case albumRegular             // 在iPhone中自己创建的相册
 
 case albumSyncedEvent         // 从iPhoto(就是现在的图片app)中导入图片到设备
 
 case albumSyncedFaces         // 从图片app中导入的人物照片
 
 case albumSyncedAlbum         // 从图片app导入的相册
 
 case albumImported            // 从其他的相机或者存储设备导入的相册
 
 
 // PHAssetCollectionTypeAlbum shared subtypes
 case albumMyPhotoStream    // 照片流,照片流和iCloud有关,如果在设置里关闭了iCloud开关,就获取不到了
 
 case albumCloudShared      // iCloud的共享相册,点击照片上的共享tab创建后就能拿到了,但是前提是你要在设置中打开iCloud的共享开关(打开后才能看见共享tab)
 
 
 // PHAssetCollectionTypeSmartAlbum subtypes
 case smartAlbumGeneric      //一般的照片
 
 case smartAlbumPanoramas    // 全景图、全景照片
 
 case smartAlbumVideos       // 视频
 
 case smartAlbumFavorites    // 标记为喜欢、收藏
 
 case smartAlbumTimelapses   // 延时拍摄、定时拍摄
 
 case smartAlbumAllHidden    // 隐藏的
 
 case smartAlbumRecentlyAdded  // 最近添加的、近期添加
 
 case smartAlbumBursts         // 连拍
 
 case smartAlbumSlomoVideos    // 慢动作视频
 
 case smartAlbumUserLibrary    // 相机胶卷
 
 @available(iOS 9.0, *)
 case smartAlbumSelfPortraits    // 使用前置摄像头拍摄的作品
 
 @available(iOS 9.0, *)
 case smartAlbumScreenshots      // 屏幕截图
 
 @available(iOS 10.2, *)
 case smartAlbumDepthEffect      // 使用深度摄像模式拍的照片
 
 @available(iOS 10.3, *)
 case smartAlbumLivePhotos       //Live Photo资源
 
 @available(iOS 11.0, *)
 case smartAlbumAnimated
 
 @available(iOS 11.0, *)
 case smartAlbumLongExposures
 
 // Used for fetching, if you don't care about the exact subtype
 case any
 }

请求相册和相机权限


 /*使用说明

 需要在Info.plist 中添加

 Privacy - Photo Library Additions Usage Description 添加图片

 Privacy - Photo Library Usage Description  读取图片*/
import Foundation
import Photos
import AVFoundation

public enum PhotoAuthorizationStatus:String{
    case photoNotDetermined = "用户尚未对相册权限做出选择"
    case photoRestricted    = "用户无权访问相册"
    case photoDenied        = "用户拒绝访问相册"
    case photoAuthorized    = "用户允许访问相册"
}

public enum CameraAuthorizationStatus:String{
    case cameraNotDetermined = "用户尚未对相机权限做出选择"
    case cameraRestricted    = "用户无权访问相机"
    case cameraDenied        = "用户拒绝访问相机"
    case cameraAuthorized    = "用户允许访问相机"
}


struct AuthorizationManager{
    public static var photoStatus:PhotoAuthorizationStatus{
        let status = PHPhotoLibrary.authorizationStatus()
        var photoStatus: PhotoAuthorizationStatus = .photoNotDetermined
        switch status {
        case .notDetermined:
            photoStatus = .photoNotDetermined
        case .restricted:
            photoStatus = .photoRestricted
        case .denied:
            photoStatus = .photoDenied
        case .authorized:
            photoStatus = .photoAuthorized
        @unknown default:
            photoStatus = .photoNotDetermined
        }
        return photoStatus
    }
    
    
    public static var cameraStatus:CameraAuthorizationStatus{
        let status =  AVCaptureDevice.authorizationStatus(for: .video)
        var cameraStatus: CameraAuthorizationStatus = .cameraNotDetermined
        switch status {
        case .notDetermined:
            cameraStatus = .cameraNotDetermined
        case .restricted:
            cameraStatus = .cameraRestricted
        case .denied:
            cameraStatus = .cameraDenied
        case .authorized:
            cameraStatus = .cameraAuthorized
        @unknown default:
            cameraStatus = .cameraNotDetermined
        }
        return cameraStatus
    }
    
    public static func checkPhotoAuthorization(complition:@escaping (PhotoAuthorizationStatus)->Void){
        if photoStatus == .photoAuthorized{
            complition(.photoAuthorized)
        }
        requestPhotoAuthorization(complition: complition)
    }
    
    
    public static func requestPhotoAuthorization(complition:@escaping (PhotoAuthorizationStatus)->Void){
        var photoStatus:PhotoAuthorizationStatus = .photoNotDetermined
        PHPhotoLibrary.requestAuthorization { (status) in
            switch status{
            case .notDetermined:
                photoStatus = .photoNotDetermined
            case .restricted:
                photoStatus = .photoRestricted
            case .denied:
                photoStatus = .photoDenied
            case .authorized:
                photoStatus = .photoAuthorized
            @unknown default:
                photoStatus = .photoNotDetermined
            }
            DispatchQueue.main.async {
                complition(photoStatus)
            }
        }
    }
    
    public  static func checkCameraAuthorization(complition:@escaping (CameraAuthorizationStatus)->Void){
        if cameraStatus == .cameraAuthorized{
            complition(.cameraAuthorized)
        }
        requestCameraAuthorization(complition: complition)
    }
    
    public static func requestCameraAuthorization(complition:@escaping (CameraAuthorizationStatus)->Void){
        var cameraStatus: CameraAuthorizationStatus = .cameraNotDetermined
        AVCaptureDevice.requestAccess(for: .video) { (isSuccess) in
            if isSuccess{
                cameraStatus = .cameraAuthorized
            }else{
                cameraStatus = .cameraDenied
            }
            DispatchQueue.main.async {
               complition(cameraStatus)
            }
        }
    }
    
}


获取相册的图片

 
import UIKit
import Photos
open class LQPhotoManager:NSObject {
    public weak var delegate:LQPhotoManagerDelegate?
    public init(photoDelegate:LQPhotoManagerDelegate?){
        delegate = photoDelegate
        super.init()
        PHPhotoLibrary.shared().register(self)
    }
    
    public func unregisterChangeObserver(){
        PHPhotoLibrary.shared().unregisterChangeObserver(self)
    }

    public func requestPhotosData(){
        AuthorizationManager.checkPhotoAuthorization { (status) in
            self.notifyObserver(status: status)
        }
    }
}



/*
 可以使用PHChnage下面的方法去监听发生的变化
 public func changeDetails<T>(for fetchResult: PHFetchResult<T>) -> PHFetchResultChangeDetails<T>? where T : PHObject
 
 通过PHFetchResultChangeDetails的下面的发生的变化
 //
 open var fetchResultBeforeChanges: PHFetchResult<ObjectType> { get }
 
 open var fetchResultAfterChanges: PHFetchResult<ObjectType> { get }
 */
public protocol LQPhotoManagerDelegate:AnyObject{
    func photosDataDidChange(changeInstance: PHChange)
    func photosAuthorizationChange(status:PhotoAuthorizationStatus)
}

extension LQPhotoManager{
    /// 获取相册
    ///
    /// - Parameters:
    ///   - type: 相册类型
    ///   - subtype: 哪种类型拍摄的
    ///   - options: 筛选条件
    public class func getAlbum(with type: PHAssetCollectionType, subtype: PHAssetCollectionSubtype, options: PHFetchOptions? = nil)-> PHFetchResult<PHAssetCollection>{
        return PHAssetCollection.fetchAssetCollections(with: PHAssetCollectionType.album, subtype: PHAssetCollectionSubtype.any, options: options)
    }
    
    
    
    /// 创建相册
    ///
    /// - Parameter albumName: 相册的名字
    public class  func createAlbum(albumName:String?) {
        guard let name = albumName else{
            return
        }
        PHPhotoLibrary.shared().performChanges({
            PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: name)
        }) { (success, error) in
            if !success{
                print("创建相册失败 错误信息:\(error?.localizedDescription ?? "没有错误信息")")
            }
        }
    }
    
    /// 删除多个相册
    ///
    /// - Parameter albumsToBeDeleted: [PHAssetCollection]?
    public class func deleteAlbums(albumsToBeDeleted: [PHAssetCollection]?) {
        guard let albums = albumsToBeDeleted else {
            return
        }
        PHPhotoLibrary.shared().performChanges({
            PHAssetCollectionChangeRequest.deleteAssetCollections(albums as NSFastEnumeration)
        }) { (success, error) in
            if !success{
                print("删除多个相册失败 错误信息:\(error?.localizedDescription ?? "没有错误信息")")
            }
        }
    }
    
    /// 获取相册中的照片
    ///
    /// - Parameter album: PHAssetCollection
    /// - Returns: PHFetchResult<PHAsset>
    public class func getPhotosFromAlbum(album:PHAssetCollection?) -> PHFetchResult<PHAsset>? {
        guard let al = album else {
            return nil
        }
        return PHAsset.fetchAssets(in: al, options: nil)
    }
    
    
    
    /// 将图片加入相册
    ///
    /// - Parameters:
    ///   - image: UIImage
    ///   - toAlbum: PHAssetCollection
    public class func addImage(image:UIImage?,toAlbum:PHAssetCollection?){
        guard let img = image,let album = toAlbum  else {
            return
        }
        
        PHPhotoLibrary.shared().performChanges({
            let addImageRequest = PHAssetChangeRequest.creationRequestForAsset(from: img)
            let addedImagePlaceholder = addImageRequest.placeholderForCreatedAsset
            let addImageToAlbum = PHAssetCollectionChangeRequest.init(for: album)
            addImageToAlbum?.addAssets([addedImagePlaceholder] as NSFastEnumeration)
        }) { (success, error) in
            if !success{
                print("将图片加入相册失败 错误信息:\(error?.localizedDescription ?? "没有错误信息")")
            }
        }
    }
    
    
    /// 将照片从相册中删除
    ///
    /// - Parameter photoAssets: [PHAsset]
    public class func deleteImagesFromAlbum(photoAssets:[PHAsset]?){
        guard let photos = photoAssets else {
            return
        }
        
        PHPhotoLibrary.shared().performChanges({
            PHAssetChangeRequest.deleteAssets(photos as NSFastEnumeration)
        }) { (success, error) in
            if !success{
                print("将图片加入相册失败 错误信息:\(error?.localizedDescription ?? "没有错误信息")")
            }
        }
    }
    
    
    /// 将PHAsset 转成Image
    ///
    /// - Parameters:
    ///   - photoAsset: PHAsset
    ///   - imageSize:  图片尺寸
    ///   - contentMode: 拉伸模式
    ///   - resultHandler: 转换成功后的回调
    public class func changePHAssetToImage(photoAsset:PHAsset?,contentMode:PHImageContentMode = PHImageContentMode.aspectFill,resultHandler: @escaping (UIImage?, [AnyHashable : Any]?) -> Void) {
        guard let photoA = photoAsset else {
            return
        }
        
        let imageManager =  PHImageManager.default()
        imageManager.requestImage(for: photoA, targetSize: CGSize.init(width: photoA.pixelWidth, height: photoA.pixelHeight), contentMode: contentMode, options: nil) { (image, imageInfo) in
            resultHandler(image,imageInfo)
        }
    }
    
    internal func notifyObserver(status:PhotoAuthorizationStatus){
        DispatchQueue.main.async {
            self.delegate?.photosAuthorizationChange(status: status)
        }
    }
}


extension LQPhotoManager:PHPhotoLibraryChangeObserver{
    public func photoLibraryDidChange(_ changeInstance: PHChange) {
        DispatchQueue.main.async {
            self.delegate?.photosDataDidChange(changeInstance: changeInstance)
        }
    }
}

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

推荐阅读更多精彩内容

  • 野狗群的首领雌犬红桃心因留恋而没有在交配期后将公狗赛豹尾赶走,造成了自己的妹妹雌犬白桃花也怀上了孩子。姐妹的情谊遭...
    Zhiya阅读 528评论 0 1
  • 什么是长生? 生是哪个生?生命,还是生存? 如果是生存,那么,长生只和寿命长短有关,顶多在加一个活的滋不滋润、自在...
    天天星星点点阅读 490评论 0 4
  • 暑假期间,孩儿们都回来了,他们晚睡晚起,我也跟着作息不正常起来,这两个月,基本上是每天睡觉都要超过十二点半的。 晚...
    文晓玲阅读 230评论 6 3
  • L,如果此生我们错在相逢,能不能求一个善终。今后你有你的山高水长,我有我的静水流深。 但愿在平行时空里的我们是幸福...
    白馥祎013阅读 235评论 0 0
  • 西雅图的雪 2016年12月9日。之前据 Jack Wu 西雅图在线报道,一场冬季风暴即将侵入西雅图地区。气象预报...
    闻丁阅读 561评论 0 6