我有一个全新的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
方法签名,似乎没有一个允许保存到自定义相册。
答案 0 :(得分:31)
最新的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)
}
}
答案 1 :(得分:30)
我想出了这个单例类来处理它:
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。并在必要时请求授权。
答案 2 :(得分:10)
这是一个更新版本,适用于Swift 2.1,并且避免了在第一次启动时未创建相册并且未保存图像的错误(首次请求/授予写入照片库的授权时)。 / p>
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)
}
}
答案 3 :(得分:7)
以前的答案非常棒,对我帮助很大,但在第一次通话时仍然存在保存图像的问题。 以下解决方案并非完全干净,但解决了这个问题。适用于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)
}
}
}
}
答案 4 :(得分:6)
我发现这里提出的一些解决方案正在运行,但我想重写一个可重用的版本。以下是您使用它的方式:
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
}
}
答案 5 :(得分:4)
使用Swift 5编写的100%工作和完善的解决方案。完成块和错误得到正确处理。我之所以改用普通班,是因为我只在应用的特定位置需要它,但是如果您大部分使用它,则可以转换为Singleton。
class PhotoManager {
private var albumName: String
private var album: PHAssetCollection?
init(albumName: String) {
self.albumName = albumName
if let album = getAlbum() {
self.album = album
return
}
}
private func getAlbum() -> PHAssetCollection? {
let options = PHFetchOptions()
options.predicate = NSPredicate(format: "title = %@", albumName)
let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: options)
return collection.firstObject ?? nil
}
private func createAlbum(completion: @escaping (Bool) -> ()) {
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: self.albumName)
}, completionHandler: { (result, error) in
if let error = error {
print("error: \(error.localizedDescription)")
} else {
self.album = self.getAlbum()
completion(result)
}
})
}
private func add(image: UIImage, completion: @escaping (Bool, Error?) -> ()) {
PHPhotoLibrary.shared().performChanges({
let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
if let album = self.album, let placeholder = assetChangeRequest.placeholderForCreatedAsset {
let albumChangeRequest = PHAssetCollectionChangeRequest(for: album)
let enumeration = NSArray(object: placeholder)
albumChangeRequest?.addAssets(enumeration)
}
}, completionHandler: { (result, error) in
completion(result, error)
})
}
func save(_ image: UIImage, completion: @escaping (Bool, Error?) -> ()) {
PHPhotoLibrary.requestAuthorization { status in
guard status == .authorized else {
// fail and redirect to app settings
return
}
if let _ = self.album {
self.add(image: image) { (result, error) in
completion(result, error)
}
return
}
self.createAlbum(completion: { _ in
self.add(image: image) { (result, error) in
completion(result, error)
}
})
}
}
}
答案 6 :(得分:3)
对于那些使用Swift 4寻找单功能解决方案的人,我已经将上面的一些代码压缩为一个函数,该函数只接受UIImage,String类型的相册名称和指示成功/失败的回调。
注意:此功能更复杂,因此它的运行时间显然比以前的解决方案慢,但为方便其他人,我将其发布在这里。
func save(image:UIImage, toAlbum:String, withCallback:((Bool)->Void)? = nil) {
func fetchAssetCollection(forAlbum:String) -> PHAssetCollection! {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %@", forAlbum)
let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
if let _: AnyObject = collection.firstObject {
return collection.firstObject
}
return nil
}
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: toAlbum) // create an asset collection with the album name
}) { success, error in
if success {
if success, let assetCollection = fetchAssetCollection(forAlbum: toAlbum) {
PHPhotoLibrary.shared().performChanges({
let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
let assetPlaceholder = assetChangeRequest.placeholderForCreatedAsset
let albumChangeRequest = PHAssetCollectionChangeRequest(for: assetCollection)
let assetEnumeration:NSArray = [assetPlaceholder!]
albumChangeRequest!.addAssets(assetEnumeration)
}, completionHandler: { (_ didComplete:Bool, _ error:Error?) -> Void in
if withCallback != nil {
withCallback!(didComplete && error == nil)
}
})
} else {
if withCallback != nil {
// Failure to save
withCallback!(false)
}
}
} else {
if withCallback != nil {
// Failure to save
withCallback!(false)
}
}
}
}
答案 7 :(得分:2)
如果您对一种面向协议的方法感兴趣,该方法允许简单地保存到具有与Swift 4最新的不同名称的多个专辑,并避免使用单身,请继续阅读。
此方法检查并获取用户授权,检查或创建相册,然后将图像保存到请求的相册。如果在任何时候触发错误,则运行完成块并显示相应的错误。
这种方法的好处是,一旦创建实例,就不会提示用户进行照片访问,而是在他们实际尝试保存图像时会提示他们,如果需要授权的话。
此方法还允许您定义一个非常简单的类,该类封装相册,符合PhotoAlbumHandler协议,从而免费获取所有相册交互逻辑,如下所示:
class PhotoAlbum: PhotoAlbumHandler {
var albumName: String
init(named: String) {
albumName = named
}
}
然后,您还可以创建管理和封装所有相册的枚举。添加对另一个专辑的支持就像在枚举中添加新案例并定义相应的albumName一样简单。
像这样:
public enum PhotoAlbums {
case landscapes
case portraits
var albumName: String {
switch self {
case .landscapes: return "Landscapes"
case .portraits: return "Portraits"
}
}
func album() -> PhotoAlbumHandler {
return PhotoAlbum.init(named: albumName)
}
}
使用此方法可以轻松管理您的相册,在viewModel(或视图控制器,如果您不使用视图模型)中,您可以创建对相册的引用,如下所示:
let landscapeAlbum = PhotoAlbums.landscapes.album()
let portraitAlbum = PhotoAlbums.portraits.album()
然后将图像保存到其中一个相册中,您可以执行以下操作:
let photo: UIImage = UIImage.init(named: "somePhotoName")
landscapeAlbum.save(photo) { (error) in
DispatchQueue.main.async {
if let error = error {
// show alert with error message or...???
self.label.text = error.message
return
}
self.label.text = "Saved image to album"
}
}
对于错误处理,我选择在错误枚举中封装任何可能的错误:
public enum PhotoAlbumHandlerError {
case unauthorized
case authCancelled
case albumNotExists
case saveFailed
case unknown
var title: String {
return "Photo Save Error"
}
var message: String {
switch self {
case .unauthorized:
return "Not authorized to access photos. Enable photo access in the 'Settings' app to continue."
case .authCancelled:
return "The authorization process was cancelled. You will not be able to save to your photo albums without authorizing access."
case .albumNotExists:
return "Unable to create or find the specified album."
case .saveFailed:
return "Failed to save specified image."
case .unknown:
return "An unknown error occured."
}
}
}
定义接口的协议和处理与系统相册功能交互的协议扩展在此处:
import Photos
public protocol PhotoAlbumHandler: class {
var albumName: String { get set }
func save(_ photo: UIImage, completion: @escaping (PhotoAlbumHandlerError?) -> Void)
}
extension PhotoAlbumHandler {
func save(_ photo: UIImage, completion: @escaping (PhotoAlbumHandlerError?) -> Void) {
// Check for permission
guard PHPhotoLibrary.authorizationStatus() == .authorized else {
// not authorized, prompt for access
PHPhotoLibrary.requestAuthorization({ [weak self] status in
// not authorized, end with error
guard let strongself = self, status == .authorized else {
completion(.authCancelled)
return
}
// received authorization, try to save photo to album
strongself.save(photo, completion: completion)
})
return
}
// check for album, create if not exists
guard let album = fetchAlbum(named: albumName) else {
// album does not exist, create album now
createAlbum(named: albumName, completion: { [weak self] success, error in
// album not created, end with error
guard let strongself = self, success == true, error == nil else {
completion(.albumNotExists)
return
}
// album created, run through again
strongself.save(photo, completion: completion)
})
return
}
// save the photo now... we have permission and the desired album
insert(photo: photo, in: album, completion: { success, error in
guard success == true, error == nil else {
completion(.saveFailed)
return
}
// finish with no error
completion(nil)
})
}
internal func fetchAlbum(named: String) -> PHAssetCollection? {
let options = PHFetchOptions()
options.predicate = NSPredicate(format: "title = %@", named)
let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: options)
guard let album = collection.firstObject else {
return nil
}
return album
}
internal func createAlbum(named: String, completion: @escaping (Bool, Error?) -> Void) {
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: named)
}, completionHandler: completion)
}
internal func insert(photo: UIImage, in collection: PHAssetCollection, completion: @escaping (Bool, Error?) -> Void) {
PHPhotoLibrary.shared().performChanges({
let request = PHAssetChangeRequest.creationRequestForAsset(from: photo)
request.creationDate = NSDate.init() as Date
guard let assetPlaceHolder = request.placeholderForCreatedAsset,
let albumChangeRequest = PHAssetCollectionChangeRequest(for: collection) else {
return
}
let enumeration: NSArray = [assetPlaceHolder]
albumChangeRequest.addAssets(enumeration)
}, completionHandler: completion)
}
}
如果您想查看示例Xcode项目,可以在此处找到:https://github.com/appteur/ios_photo_album_sample
答案 8 :(得分:2)
Swift 5更新,添加了完成处理程序。
用法:
CustomPhotoAlbum.sharedInstance.save(image, completion: { result, error in
if let e = error {
// handle error
return
}
// save successful, do something (such as inform user)
})
单班:
import Foundation
import Photos
import UIKit
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 \(String(describing: 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, completion: @escaping ((Bool, Error?) -> ())) {
if assetCollection == nil {
// if there was an error upstream, skip the save
return
}
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: { result, error in
completion(result, error)
})
}
}
答案 9 :(得分:1)
改进@Damien answer。适用于UIImage
和视频(带网址)。 Swift4
已经过测试:
import Photos
class MyAwesomeAlbum: NSObject {
static let albumName = "My Awesome Album"
static let shared = MyAwesomeAlbum()
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 { (success) in
if success {
completion(true)
} else {
completion(false)
}
}
}
else {
completion(false)
}
}
private func createAlbumIfNeeded(completion: @escaping ((_ success: Bool) -> Void)) {
if let assetCollection = fetchAssetCollectionForAlbum() {
// Album already exists
self.assetCollection = assetCollection
completion(true)
} else {
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: MyAwesomeAlbum.albumName) // create an asset collection with the album name
}) { success, error in
if success {
self.assetCollection = self.fetchAssetCollectionForAlbum()
completion(true)
} else {
// Unable to create album
completion(false)
}
}
}
}
private func fetchAssetCollectionForAlbum() -> PHAssetCollection? {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %@", MyAwesomeAlbum.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
if let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection) {
let enumeration: NSArray = [assetPlaceHolder!]
albumChangeRequest.addAssets(enumeration)
}
}, completionHandler: { (success, error) in
if success {
print("Successfully saved image to Camera Roll.")
} else {
print("Error writing to image library: \(error!.localizedDescription)")
}
})
}
}
}
func saveMovieToLibrary(movieURL: URL) {
self.checkAuthorizationWithHandler { (success) in
if success, self.assetCollection != nil {
PHPhotoLibrary.shared().performChanges({
if let assetChangeRequest = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: movieURL) {
let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset
if let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection) {
let enumeration: NSArray = [assetPlaceHolder!]
albumChangeRequest.addAssets(enumeration)
}
}
}, completionHandler: { (success, error) in
if success {
print("Successfully saved video to Camera Roll.")
} else {
print("Error writing to movie library: \(error!.localizedDescription)")
}
})
}
}
}
}
用法:
MyAwesomeAlbum.shared.save(image: image)
或
MyAwesomeAlbum.shared.saveMovieToLibrary(movieURL: url)
答案 10 :(得分:1)
谢谢,试图使用此代码,但发现了一些逻辑错误。这是清理过的代码
import Photos
class CustomPhotoAlbum: NSObject {
static let albumName = Bundle.main.infoDictionary![kCFBundleNameKey as String] as! String
static let shared = CustomPhotoAlbum()
private lazy var assetCollection = fetchAssetCollectionForAlbum()
private override init() {
super.init()
}
private func checkAuthorizationWithHandler(completion: @escaping ((_ success: Bool) -> Void)) {
switch PHPhotoLibrary.authorizationStatus() {
case .authorized:
completion(true)
case .notDetermined:
PHPhotoLibrary.requestAuthorization(){ (status) in
self.checkAuthorizationWithHandler(completion: completion)
}
case .denied, .restricted:
completion(false)
}
}
private func fetchAssetCollectionForAlbum() -> PHAssetCollection? {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %@", CustomPhotoAlbum.albumName)
let fetch = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
return fetch.firstObject
}
func save(image: UIImage) {
func saveIt(_ validAssets: PHAssetCollection){
PHPhotoLibrary.shared().performChanges({
let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset
let albumChangeRequest = PHAssetCollectionChangeRequest(for: validAssets)
let enumeration: NSArray = [assetPlaceHolder!]
albumChangeRequest!.addAssets(enumeration)
}, completionHandler: nil)
}
self.checkAuthorizationWithHandler { (success) in
if success {
if let validAssets = self.assetCollection { // Album already exists
saveIt(validAssets)
} else { // create an asset collection with the album name
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: CustomPhotoAlbum.albumName)
}) { success, error in
if success, let validAssets = self.fetchAssetCollectionForAlbum() {
self.assetCollection = validAssets
saveIt(validAssets)
} else {
// TODO: send user message "Sorry, unable to create album and save image..."
}
}
}
}
}
}
}
答案 11 :(得分:0)
即使在修复后,我的PhotoAlbum仍无法用于第一张图片,如果我想一次保存多张图片,我最终会有多张空白相册。所以我升级了课程,我只在创建专辑后才保存表情符号。
新版本:
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
}
}
}
}
}
}
}
如果您想一次保存多个图像,这是我的代码。这里的关键是延迟保存不是第一个的其他图像,因为我们必须先创建相册。 (否则我们最终会出现重复的专辑,因为所有保存过程都会尝试创建自定义专辑)。这是我的应用程序中的代码,因此您可以理解逻辑:
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)
}
}
答案 12 :(得分:0)
快速4和5
在按钮上单击,就像这样
@IBAction func btnSave(_ sender: Any) {
createAlbum()
self.saveGIFInAlbum(url: gifUrl)
}
func createAlbum() {
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.createAlbumIfNeeded()
} 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.createAlbumIfNeeded()
} else {
print("should really prompt the user to let them know it's failed")
}
}
func createAlbumIfNeeded() {
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: "PicBloom") // create an asset collection with the album name
}) { success, error in
if success {
self.assetCollection = self.fetchAssetCollectionForAlbum()
} else {
print("error \(String(describing: error))")
}
}
}
func fetchAssetCollectionForAlbum() -> PHAssetCollection? {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %@", "PicBloom")
let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
if let _: AnyObject = collection.firstObject {
return collection.firstObject
}
return nil
}
func saveGIFInAlbum(url: URL){
PHPhotoLibrary.shared().performChanges({
let assetChangeRequest = PHAssetChangeRequest.creationRequestForAssetFromImage(atFileURL: url)
let assetPlaceHolder = assetChangeRequest?.placeholderForCreatedAsset
if(self.assetCollection != nil) {
let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection)
let enumeration: NSArray = [assetPlaceHolder!]
albumChangeRequest!.addAssets(enumeration)
}
}, completionHandler: { success, error in
DispatchQueue.main.async {
if success {
let message = "Gif has been saved to your gallery!"
let alert = UIAlertController(title: "", message: message, preferredStyle: .alert)
self.present(alert, animated: true)
let duration: Double = 1.5
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + duration) {
alert.dismiss(animated: true)
}
}else if error != nil {
print("handle error since couldn't save GIF")
}
}
})
}