如何将图像保存到自定义相册?

我有一个全新的iOS应用程序生成图像,并让用户保存到相机SavedPhotosAlbum。 但是,我想要做一些像Snapchat和Frontback,并将这些图像也保存到一个自定义的专辑。

所以这是我现在的代码:

let imageToSave = self.currentPreviewImage let softwareContext = CIContext(options:[kCIContextUseSoftwareRenderer: true]) let cgimg = softwareContext.createCGImage(imageToSave, fromRect:imageToSave.extent()) ALAssetsLibrary().writeImageToSavedPhotosAlbum(cgimg, metadata:imageToSave.properties(), completionBlock:nil) 

我已经在Objective-C中看到了一些人做这样的例子,但是没有什么可以翻译成Swift的,而且我检查了writeImageToSavedPhotosAlbum方法签名,并且他们都没有允许保存到自定义相册。

我想出了这个单身人士来处理这个问题:

 import Photos class CustomPhotoAlbum { static let albumName = "Flashpod" static let sharedInstance = CustomPhotoAlbum() var assetCollection: PHAssetCollection! init() { func fetchAssetCollectionForAlbum() -> PHAssetCollection! { let fetchOptions = PHFetchOptions() fetchOptions.predicate = NSPredicate(format: "title = %@", CustomPhotoAlbum.albumName) let collection = PHAssetCollection.fetchAssetCollectionsWithType(.Album, subtype: .Any, options: fetchOptions) if let firstObject: AnyObject = collection.firstObject { return collection.firstObject as! PHAssetCollection } return nil } if let assetCollection = fetchAssetCollectionForAlbum() { self.assetCollection = assetCollection return } PHPhotoLibrary.sharedPhotoLibrary().performChanges({ PHAssetCollectionChangeRequest.creationRequestForAssetCollectionWithTitle(CustomPhotoAlbum.albumName) }) { success, _ in if success { self.assetCollection = fetchAssetCollectionForAlbum() } } } func saveImage(image: UIImage) { if assetCollection == nil { return // If there was an error upstream, skip the save. } PHPhotoLibrary.sharedPhotoLibrary().performChanges({ let assetChangeRequest = PHAssetChangeRequest.creationRequestForAssetFromImage(image) let assetPlaceholder = assetChangeRequest.placeholderForCreatedAsset let albumChangeRequest = PHAssetCollectionChangeRequest(forAssetCollection: self.assetCollection) albumChangeRequest.addAssets([assetPlaceholder]) }, completionHandler: nil) } } 

当你第一次实例化这个类的时候,自定义相册将会被创build,如果它不存在的话。 你可以像这样保存图像:

 CustomPhotoAlbum.sharedInstance.saveImage(image) 

注:CustomPhotoAlbum类假定应用程序已经有权访问照片库。 处理权限有点超出了这个问题/答案的范围。 所以在使用它之前,请确保PHPhotoLibrary.authorizationStatus()== .Authorize。 如有必要,请求授权。

最新的Swift 3.0语法。 🙂

 import Foundation import Photos class CustomPhotoAlbum: NSObject { static let albumName = "Album Name" static let sharedInstance = CustomPhotoAlbum() var assetCollection: PHAssetCollection! override init() { super.init() if let assetCollection = fetchAssetCollectionForAlbum() { self.assetCollection = assetCollection return } if PHPhotoLibrary.authorizationStatus() != PHAuthorizationStatus.authorized { PHPhotoLibrary.requestAuthorization({ (status: PHAuthorizationStatus) -> Void in () }) } if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized { self.createAlbum() } else { PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler) } } func requestAuthorizationHandler(status: PHAuthorizationStatus) { if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized { // ideally this ensures the creation of the photo album even if authorization wasn't prompted till after init was done print("trying again to create the album") self.createAlbum() } else { print("should really prompt the user to let them know it's failed") } } func createAlbum() { PHPhotoLibrary.shared().performChanges({ PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: CustomPhotoAlbum.albumName) // create an asset collection with the album name }) { success, error in if success { self.assetCollection = self.fetchAssetCollectionForAlbum() } else { print("error \(error)") } } } func fetchAssetCollectionForAlbum() -> PHAssetCollection? { let fetchOptions = PHFetchOptions() fetchOptions.predicate = NSPredicate(format: "title = %@", CustomPhotoAlbum.albumName) let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions) if let _: AnyObject = collection.firstObject { return collection.firstObject } return nil } func save(image: UIImage) { if assetCollection == nil { return // if there was an error upstream, skip the save } PHPhotoLibrary.shared().performChanges({ let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image) let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection) let enumeration: NSArray = [assetPlaceHolder!] albumChangeRequest!.addAssets(enumeration) }, completionHandler: nil) } } 

这是一个更新的版本,在Swift 2.1中起作用,并且避免了在没有创build相册并且图像在第一次启动(当首次请求/授予写入到照片库的授权时)时不保存的错误。

 class CustomPhotoAlbum: NSObject { static let albumName = "Name of Custom Album" static let sharedInstance = CustomPhotoAlbum() var assetCollection: PHAssetCollection! override init() { super.init() if let assetCollection = fetchAssetCollectionForAlbum() { self.assetCollection = assetCollection return } if PHPhotoLibrary.authorizationStatus() != PHAuthorizationStatus.Authorized { PHPhotoLibrary.requestAuthorization({ (status: PHAuthorizationStatus) -> Void in status }) } if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.Authorized { self.createAlbum() } else { PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler) } } func requestAuthorizationHandler(status: PHAuthorizationStatus) { if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.Authorized { // ideally this ensures the creation of the photo album even if authorization wasn't prompted till after init was done print("trying again to create the album") self.createAlbum() } else { print("should really prompt the user to let them know it's failed") } } func createAlbum() { PHPhotoLibrary.sharedPhotoLibrary().performChanges({ PHAssetCollectionChangeRequest.creationRequestForAssetCollectionWithTitle(CustomPhotoAlbum.albumName) // create an asset collection with the album name }) { success, error in if success { self.assetCollection = self.fetchAssetCollectionForAlbum() } else { print("error \(error)") } } } func fetchAssetCollectionForAlbum() -> PHAssetCollection! { let fetchOptions = PHFetchOptions() fetchOptions.predicate = NSPredicate(format: "title = %@", CustomPhotoAlbum.albumName) let collection = PHAssetCollection.fetchAssetCollectionsWithType(.Album, subtype: .Any, options: fetchOptions) if let _: AnyObject = collection.firstObject { return collection.firstObject as! PHAssetCollection } return nil } func saveImage(image: UIImage, metadata: NSDictionary) { if assetCollection == nil { return // if there was an error upstream, skip the save } PHPhotoLibrary.sharedPhotoLibrary().performChanges({ let assetChangeRequest = PHAssetChangeRequest.creationRequestForAssetFromImage(image) let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset let albumChangeRequest = PHAssetCollectionChangeRequest(forAssetCollection: self.assetCollection) albumChangeRequest!.addAssets([assetPlaceHolder!]) }, completionHandler: nil) } } 

以前的答案很棒,帮了我很多,但是在第一个电话上保存图片仍然有问题。 下面的解决scheme并不完美,但解决了这个问题。 适用于Swift 3/4。

 import Photos class CustomPhotoAlbum: NSObject { static let albumName = "Album name" static let shared = CustomPhotoAlbum() private var assetCollection: PHAssetCollection! private override init() { super.init() if let assetCollection = fetchAssetCollectionForAlbum() { self.assetCollection = assetCollection return } } private func checkAuthorizationWithHandler(completion: @escaping ((_ success: Bool) -> Void)) { if PHPhotoLibrary.authorizationStatus() == .notDetermined { PHPhotoLibrary.requestAuthorization({ (status) in self.checkAuthorizationWithHandler(completion: completion) }) } else if PHPhotoLibrary.authorizationStatus() == .authorized { self.createAlbumIfNeeded() completion(true) } else { completion(false) } } private func createAlbumIfNeeded() { if let assetCollection = fetchAssetCollectionForAlbum() { // Album already exists self.assetCollection = assetCollection } else { PHPhotoLibrary.shared().performChanges({ PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: CustomPhotoAlbum.albumName) // create an asset collection with the album name }) { success, error in if success { self.assetCollection = self.fetchAssetCollectionForAlbum() } else { // Unable to create album } } } } private func fetchAssetCollectionForAlbum() -> PHAssetCollection? { let fetchOptions = PHFetchOptions() fetchOptions.predicate = NSPredicate(format: "title = %@", CustomPhotoAlbum.albumName) let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions) if let _: AnyObject = collection.firstObject { return collection.firstObject } return nil } func save(image: UIImage) { self.checkAuthorizationWithHandler { (success) in if success, self.assetCollection != nil { PHPhotoLibrary.shared().performChanges({ let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image) let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection) let enumeration: NSArray = [assetPlaceHolder!] albumChangeRequest!.addAssets(enumeration) }, completionHandler: nil) } } } } 

我发现这里提出了一些解决scheme,但是我想重写一个可重用的版本。 这里是你如何使用它:

 let image = // this is your image object // Use the shared instance that has the default album name CustomPhotoAlbum.shared.save(image) // Use a custom album name let album = CustomPhotoAlbum("some title") album.save(image) 

保存图像时,请求用户的照片访问权限(如果以前授权,则立即返回),并尝试创build相册(如果该相册尚不存在)。 下面是在Swift 3中编写的完整的源代码,并与Objective-C兼容。

 // // CustomPhotoAlbum.swift // // Copyright © 2017 Et Voilapp. All rights reserved. // import Foundation import Photos @objc class CustomPhotoAlbum: NSObject { /// Default album title. static let defaultTitle = "Your title" /// Singleton static let shared = CustomPhotoAlbum(CustomPhotoAlbum.defaultTitle) /// The album title to use. private(set) var albumTitle: String /// This album's asset collection internal var assetCollection: PHAssetCollection? /// Initialize a new instance of this class. /// /// - Parameter title: Album title to use. init(_ title: String) { self.albumTitle = title super.init() } /// Save the image to this app's album. /// /// - Parameter image: Image to save. public func save(_ image: UIImage?) { guard let image = image else { return } // Request authorization and create the album requestAuthorizationIfNeeded { (_) in // If it all went well, we've got our asset collection guard let assetCollection = self.assetCollection else { return } PHPhotoLibrary.shared().performChanges({ // Make sure that there's no issue while creating the request let request = PHAssetChangeRequest.creationRequestForAsset(from: image) guard let placeholder = request.placeholderForCreatedAsset, let albumChangeRequest = PHAssetCollectionChangeRequest(for: assetCollection) else { return } let enumeration: NSArray = [placeholder] albumChangeRequest.addAssets(enumeration) }, completionHandler: nil) } } } internal extension CustomPhotoAlbum { /// Request authorization and create the album if that went well. /// /// - Parameter completion: Called upon completion. func requestAuthorizationIfNeeded(_ completion: @escaping ((_ success: Bool) -> Void)) { PHPhotoLibrary.requestAuthorization { status in guard status == .authorized else { completion(false) return } // Try to find an existing collection first so that we don't create duplicates if let collection = self.fetchAssetCollectionForAlbum() { self.assetCollection = collection completion(true) } else { self.createAlbum(completion) } } } /// Creates an asset collection with the album name. /// /// - Parameter completion: Called upon completion. func createAlbum(_ completion: @escaping ((_ success: Bool) -> Void)) { PHPhotoLibrary.shared().performChanges({ PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: self.albumTitle) }) { (success, error) in defer { completion(success) } guard error == nil else { print("error \(error!)") return } self.assetCollection = self.fetchAssetCollectionForAlbum() } } /// Fetch the asset collection matching this app's album. /// /// - Returns: An asset collection if found. func fetchAssetCollectionForAlbum() -> PHAssetCollection? { let fetchOptions = PHFetchOptions() fetchOptions.predicate = NSPredicate(format: "title = %@", albumTitle) let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions) return collection.firstObject } } 

即使在修复之后,我的PhotoAlbum仍然不能用于第一张图像,如果我想一次保存多张图像,我最终会得到多张空白相册。 所以我已经升级了课程,并且在专辑创build后我只保存了表情符号。

新版本:

 class CustomPhotoAlbum: NSObject { static let albumName = "AlbumName" static let shared = CustomPhotoAlbum() private var assetCollection: PHAssetCollection! private override init() { super.init() if let assetCollection = fetchAssetCollectionForAlbum() { self.assetCollection = assetCollection return } } private func checkAuthorizationWithHandler(completion: @escaping ((_ success: Bool) -> Void)) { if PHPhotoLibrary.authorizationStatus() == .notDetermined { PHPhotoLibrary.requestAuthorization({ (status) in self.checkAuthorizationWithHandler(completion: completion) }) } else if PHPhotoLibrary.authorizationStatus() == .authorized { self.createAlbumIfNeeded() completion(true) } else { completion(false) } } private func createAlbumIfNeeded() { /* if let assetCollection = fetchAssetCollectionForAlbum() { // Album already exists self.assetCollection = assetCollection } else { PHPhotoLibrary.shared().performChanges({ PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: CustomPhotoAlbum.albumName) // create an asset collection with the album name }) { success, error in if success { self.assetCollection = self.fetchAssetCollectionForAlbum() } else { // Unable to create album } } }*/ } private func fetchAssetCollectionForAlbum() -> PHAssetCollection? { let fetchOptions = PHFetchOptions() fetchOptions.predicate = NSPredicate(format: "title = %@", CustomPhotoAlbum.albumName) let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions) if let _: AnyObject = collection.firstObject { return collection.firstObject } return nil } func save(image: UIImage) { self.checkAuthorizationWithHandler { (success) in if success { if let assetCollection = self.fetchAssetCollectionForAlbum() { // Album already exists self.assetCollection = assetCollection PHPhotoLibrary.shared().performChanges({ let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image) let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection) let enumeration: NSArray = [assetPlaceHolder!] albumChangeRequest!.addAssets(enumeration) }, completionHandler: nil) } else { PHPhotoLibrary.shared().performChanges({ PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: CustomPhotoAlbum.albumName) // create an asset collection with the album name }) { success, error in if success { self.assetCollection = self.fetchAssetCollectionForAlbum() PHPhotoLibrary.shared().performChanges({ let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image) let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection) let enumeration: NSArray = [assetPlaceHolder!] albumChangeRequest!.addAssets(enumeration) }, completionHandler: nil) } else { // Unable to create album } } } } } } 

}

如果你想一次保存多个图像,这是我的代码。 这里的关键是延迟保存其他不是第一个的图像,因为我们必须先创build相册。 (否则我们会得到重复的相册,因为所有的保存过程都会尝试创build一个自定义相册)。 这是我的应用程序的代码,所以你可以理解的逻辑:

 var overFirstSave = false for stickerName in filenames { let url = self.getDocumentsDirectory().appendingPathComponent(stickerName as! String) do{ if !overFirstSave{ CustomPhotoAlbum.shared.save(image: UIImage(contentsOfFile: url.path)!) overFirstSave = true }else{ DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3), execute: { CustomPhotoAlbum.shared.save(image: UIImage(contentsOfFile: url.path)!) }) } }catch { print(error) } }