我有一个全新的iOS应用程序,可以生成图像并让用户将它们保存到Camera 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) } }
首次实例化该类时,如果自定义相册尚不存在,则将创建该相册.您可以保存这样的图像:
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,并且避免了在第一次启动时未创建相册并且未保存图像的错误(首次请求/授予写入照片库的授权时).
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) } }
以前的答案非常棒,对我帮助很大,但在第一次通话时仍然存在保存图像的问题.以下解决方案并非完全干净,但解决了这个问题.适用于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)
}
}
}
}
我发现这里提出了一些建议的解决方案,但是我想重写它的可重用版本。使用方法如下:
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)
保存图像时,它会请求用户进行照片访问(如果事先获得授权,则会立即返回),如果还不存在,则尝试创建相册。以下是用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 } }