puede poner oculto ocultar fotos contraseña con como clave album ios swift ios8

ios - ocultar - poner contraseña album oculto iphone



¿Cómo guardar la imagen en el álbum personalizado? (10)

Descubrí que algunas de las soluciones propuestas aquí estaban funcionando, pero quería reescribir una versión reutilizable. Así es como lo usas:

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)

Al guardar una imagen, solicita el acceso a la foto del usuario (que se devuelve inmediatamente si se ha autorizado previamente) e intenta crear un álbum si aún no existe uno. A continuación se muestra el código fuente completo escrito en Swift 3 y compatible con 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 } }

Tengo una nueva aplicación para iOS que genera imágenes y permite a los usuarios guardarlas en el Álbum de fotos guardadas de la cámara. Sin embargo, quiero hacer algo como Snapchat y Frontback, y guardar estas imágenes también en un álbum personalizado.

Así que este es mi código en este momento:

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)

He visto algunos ejemplos de personas que hacen esto en Objective-C, pero nada que pudiera traducir a Swift, y he comprobado las writeImageToSavedPhotosAlbum método writeImageToSavedPhotosAlbum y ninguna de ellas parece permitir guardar en un álbum personalizado.


Esta es una versión actualizada, funciona en Swift 2.1 y evita el error donde el álbum no se crea y las imágenes no se guardan en el primer lanzamiento (cuando se solicita / otorga la autorización para escribir en la biblioteca de fotos).

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) } }


Gracias, estaba tratando de usar este código, pero encontró algunos errores lógicos. Aquí está el código limpiado

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..." } } } } } } }


Incluso después de las correcciones, mi PhotoAlbum no funcionó para la primera imagen y si quería guardar más de una imagen a la vez, he terminado con varios álbumes vacíos. Así que actualicé la clase y solo guardo el emoji después de que el álbum haya sido creado.

Nueva versión:

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 } } } } } }

}

Si desea guardar varias imágenes a la vez, aquí está mi código para eso. La clave aquí es retrasar el guardado de las otras imágenes que no son las primeras, porque primero tenemos que crear el álbum. (De lo contrario, terminamos con álbumes duplicados, porque todos los procesos de guardado intentarán crear un álbum personalizado). Este es el código de mi aplicación, para que puedas entender la lógica:

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) } }


La última sintaxis de 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) } }


Las respuestas anteriores fueron excelentes y me ayudaron mucho, pero aún así hubo un problema al guardar la imagen en la primera llamada. La siguiente solución no está perfectamente limpia pero aborda el problema. Funciona con 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) } } } }


Mejora con la respuesta de @Damien . Funciona con UIImage y video (con url) también. Swift4 probado:

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)") } }) } } } }

Uso:

MyAwesomeAlbum.shared.save(image: image)

o

MyAwesomeAlbum.shared.saveMovieToLibrary(movieURL: url)


Se me ocurrió esta clase de singleton para manejarlo:

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) } }

Cuando crea una instancia de la clase por primera vez, el álbum personalizado se creará si no existe. Puedes guardar una imagen como esta:

CustomPhotoAlbum.sharedInstance.saveImage(image)

NOTA: La clase CustomPhotoAlbum asume que la aplicación ya tiene permiso para acceder a la Biblioteca de fotos. Tratar con los permisos está un poco fuera del alcance de esta pregunta / respuesta. Así que asegúrese de PHPhotoLibrary.authorizationStatus () == .Authorize antes de usarlo. Y solicitar autorización si es necesario.


Si está interesado en un enfoque orientado al protocolo que permita guardar de forma sencilla varios álbumes con nombres diferentes que estén actualizados con Swift 4 y evite el uso de singleton, siga leyendo.

Este enfoque comprueba y obtiene la autorización del usuario, comprueba o crea el álbum de fotos y luego guarda la imagen en el álbum solicitado. Si en algún punto se dispara un error, el bloque de finalización se ejecuta con el error correspondiente.

Una ventaja de este enfoque es que no se solicita al usuario que acceda a las fotos tan pronto como se crea la instancia, sino que se le solicita cuando realmente está intentando guardar su imagen, si se requiere autorización.

Este método también le permite definir una clase muy simple que encapsula un álbum de fotos, se ajusta al protocolo PhotoAlbumHandler y, de este modo, obtiene toda la lógica de interacción del álbum de fotos de forma gratuita, como esto:

class PhotoAlbum: PhotoAlbumHandler { var albumName: String init(named: String) { albumName = named } }

También puede crear una enumeración que administre y encapsule todos sus álbumes de fotos. Agregar soporte para otro álbum es tan simple como agregar un nuevo caso a la enumeración y definir el nombre del álbum correspondiente.

Me gusta esto:

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) } }

El uso de este enfoque hace que administrar sus álbumes de fotos sea muy fácil, en su viewModel (o en el controlador de vista si no está usando modelos de vista) puede crear referencias a sus álbumes de esta manera:

let landscapeAlbum = PhotoAlbums.landscapes.album() let portraitAlbum = PhotoAlbums.portraits.album()

Luego, para guardar una imagen en uno de los álbumes, puedes hacer algo como esto:

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" } }

Para el manejo de errores opté por encapsular cualquier error posible en una enumeración de error:

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." } } }

Aquí está el protocolo que define la interfaz y la extensión del protocolo que maneja la interacción con la funcionalidad del álbum de fotos del sistema:

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) } }

Si desea ver un proyecto Xcode de ejemplo, puede encontrar uno aquí: https://github.com/appteur/ios_photo_album_sample


Para aquellos de ustedes que buscan una solución de una sola función utilizando Swift 4, he condensado parte del código anterior en una función que simplemente toma un UIImage, un nombre de álbum de tipo String y una devolución de llamada que indica el éxito / fracaso.

Nota: esta función es más compleja, por lo que obviamente tendrá un tiempo de ejecución más lento que las soluciones anteriores, pero la he publicado aquí para conveniencia de otras personas.

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) } } } }