swift - Guardar la imagen seleccionada en CoreData
core-data uiimage (2)
Puedo seleccionar y mostrar una imagen de la biblioteca de fotos, pero mi objetivo es poder guardar esa imagen elegida o la ruta del archivo a los datos centrales para que cuando se seleccione ese registro guardado, esa imagen también se muestre.
Tengo CoreData funcionando y puedo mostrar bien el texto de CoreData, solo es la imagen que me sostiene.
@IBAction func addPic(sender: AnyObject) {
pickerController.delegate = self
pickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
// 2
self.presentViewController(pickerController, animated: true, completion: nil)
// Displays image
func imagePickerController(picker: UIImagePickerController!,didFinishPickingMediaWithInfo info: NSDictionary!){
image.image = info[UIImagePickerControllerOriginalImage] as? UIImage
self.dismissViewControllerAnimated(true, completion: nil)
Core Data no está diseñado para guardar grandes archivos binarios como imágenes. Use Document Directory en el sistema de archivos en su lugar.
Aquí hay un código de muestra para lograr eso.
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask, true).first as! String
// self.fileName is whatever the filename that you need to append to base directory here.
let path = documentsDirectory.stringByAppendingPathComponent(self.fileName)
let success = data.writeToFile(path, atomically: true)
if !success { // handle error }
Pase a Procesar la imagen para descubrir cómo convertir UIImage
a NSData
(que es lo que usa Core Data)
O descargar desde github
Configuración de datos básicos:
Configure dos entidades: Resolución completa y Miniatura. Resoluciones completas es almacenar la imagen original. Miniatura para almacenar una versión más pequeña para usar dentro de la aplicación. Puede usar una versión más pequeña en una UICollectionView
general de UICollectionView
por ejemplo.
Las imágenes se almacenan como Binary Data
en los Core Data
. El tipo correspondiente en Foundation
es NSData
. Convierte de nuevo a UIImage
con UIImage(data: newImageData)
Marque la casilla Permitir almacenamiento externo para los campos de Datos binarios. Esto guardará automáticamente las imágenes en el sistema de archivos y las referenciará en Core Data
Conecte las dos entidades, creando una relación uno a uno entre los dos.
Vaya a Editor en seleccione Crear NSManagedObjectSubclass . Esto generará archivos con Clases que representan sus Subclases de Objetos Administrados. Estos aparecerán en la estructura de su archivo de proyecto.
Configuración básica de ViewController:
Importe lo siguiente:
import UIKit
import CoreData
- Configura dos
UIButtons
y unUIImageView
en el Interface Builder - Cree dos colas de envío, una para CoreData y otra para las conversiones de UIImage
class ViewController: UIViewController {
// imageview to display loaded image
@IBOutlet weak var imageView: UIImageView!
// image picker for capture / load
let imagePicker = UIImagePickerController()
// dispatch queues
let convertQueue = dispatch_queue_create("convertQueue", DISPATCH_QUEUE_CONCURRENT)
let saveQueue = dispatch_queue_create("saveQueue", DISPATCH_QUEUE_CONCURRENT)
// moc
var managedContext : NSManagedObjectContext?
override func viewDidLoad() {
super.viewDidLoad()
imagePickerSetup() // image picker delegate and settings
coreDataSetup() // set value of moc on the right thread
}
// this function displays the imagePicker
@IBAction func capture(sender: AnyObject) { // button action
presentViewController(imagePicker, animated: true, completion: nil)
}
@IBAction func load(sender: AnyObject) { // button action
loadImages { (images) -> Void in
if let thumbnailData = images?.last?.thumbnail?.imageData {
let image = UIImage(data: thumbnailData)
self.imageView.image = image
}
}
}
}
Esta función establece un valor para managedContext
en el hilo correcto. Dado que CoreData necesita que todas las operaciones en un NSManagedObjectContext
sucedan en el mismo hilo.
extension ViewController {
func coreDataSetup() {
dispatch_sync(saveQueue) {
self.managedContext = AppDelegate().managedObjectContext
}
}
}
Extienda el UIViewController
para que se ajuste a UIImagePickerControllerDelegate
y UINavigationControllerDelegate
Estos son necesarios para UIImagePickerController
.
Cree una función de configuración y cree también la función de delegado imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?)
extension ViewController : UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerSetup() {
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.Camera
}
// When an image is "picked" it will return through this function
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
self.dismissViewControllerAnimated(true, completion: nil)
prepareImageForSaving(image)
}
}
UIImagePickerController
inmediatamente el UIImagePickerController
, de lo contrario, la aplicación parecerá congelarse.
Procesando la imagen:
Llame a esta función dentro de imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?)
.
Primero obtenga la fecha actual con
timeIntervalSince1970
. Esto devuelve unNSTimerInterval
en segundos. Esto se convierte muy bien en unDouble
. Servirá como una identificación única para las imágenes y como una forma de ordenarlas.Ahora es un buen momento para pasar a la cola separada y liberar la cola principal.
dispatch_async(convertQueue)
primero para hacer el trabajo pesado en un hilo separado.Entonces necesitas convertir el
UIImage
aNSData
esto se hace conUIImageJPEGRepresentation(image, 1)
. El1
representa la calidad donde1
es el más alto y0
es el más bajo. Devuelve un opcional así que utilicé el enlace opcional.Escala la imagen a un tamaño de miniatura deseado y también
NSData
enNSData
.
Código:
extension ViewController {
func prepareImageForSaving(image:UIImage) {
// use date as unique id
let date : Double = NSDate().timeIntervalSince1970
// dispatch with gcd.
dispatch_async(convertQueue) {
// create NSData from UIImage
guard let imageData = UIImageJPEGRepresentation(image, 1) else {
// handle failed conversion
print("jpg error")
return
}
// scale image, I chose the size of the VC because it is easy
let thumbnail = image.scale(toSize: self.view.frame.size)
guard let thumbnailData = UIImageJPEGRepresentation(thumbnail, 0.7) else {
// handle failed conversion
print("jpg error")
return
}
// send to save function
self.saveImage(imageData, thumbnailData: thumbnailData, date: date)
}
}
}
Esta función hace el ahorro real.
- Vaya al subproceso de CoreData con
dispatch_barrier_sync(saveQueue)
- Primero inserte un nuevo FullRes y un nuevo objeto Miniatura en el Contexto del Objeto Administrado.
- Establecer los valores
- Establecer la relación entre FullRes y Miniatura
- Use
do try catch
para intentar guardar - Actualice el contexto del objeto gestionado para liberar memoria
Al usar dispatch_barrier_sync(saveQueue)
estamos seguros de que podemos almacenar de manera segura una nueva imagen y de que las nuevas copias o cargas esperarán hasta que esto termine.
Código:
extension ViewController {
func saveImage(imageData:NSData, thumbnailData:NSData, date: Double) {
dispatch_barrier_sync(saveQueue) {
// create new objects in moc
guard let moc = self.managedContext else {
return
}
guard let fullRes = NSEntityDescription.insertNewObjectForEntityForName("FullRes", inManagedObjectContext: moc) as? FullRes, let thumbnail = NSEntityDescription.insertNewObjectForEntityForName("Thumbnail", inManagedObjectContext: moc) as? Thumbnail else {
// handle failed new object in moc
print("moc error")
return
}
//set image data of fullres
fullRes.imageData = imageData
//set image data of thumbnail
thumbnail.imageData = thumbnailData
thumbnail.id = date as NSNumber
thumbnail.fullRes = fullRes
// save the new objects
do {
try moc.save()
} catch {
fatalError("Failure to save context: /(error)")
}
// clear the moc
moc.refreshAllObjects()
}
}
}
Para cargar una imagen:
extension ViewController {
func loadImages(fetched:(images:[FullRes]?) -> Void) {
dispatch_async(saveQueue) {
guard let moc = self.managedContext else {
return
}
let fetchRequest = NSFetchRequest(entityName: "FullRes")
do {
let results = try moc.executeFetchRequest(fetchRequest)
let imageData = results as? [FullRes]
dispatch_async(dispatch_get_main_queue()) {
fetched(images: imageData)
}
} catch let error as NSError {
print("Could not fetch /(error), /(error.userInfo)")
return
}
}
}
}
Las funciones utilizadas para escalar la imagen:
extension CGSize {
func resizeFill(toSize: CGSize) -> CGSize {
let scale : CGFloat = (self.height / self.width) < (toSize.height / toSize.width) ? (self.height / toSize.height) : (self.width / toSize.width)
return CGSize(width: (self.width / scale), height: (self.height / scale))
}
}
extension UIImage {
func scale(toSize newSize:CGSize) -> UIImage {
// make sure the new size has the correct aspect ratio
let aspectFill = self.size.resizeFill(newSize)
UIGraphicsBeginImageContextWithOptions(aspectFill, false, 0.0);
self.drawInRect(CGRectMake(0, 0, aspectFill.width, aspectFill.height))
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}