Photos儲存、獲取、更改照片詳解

Dwyane_Coding發表於2018-01-03

圖片來源於網路

###前言: 相簿儲存到系統相簿中,通常有三種辦法:

  • 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)
複製程式碼

相關文章