Salvataggio di un’immagine selezionata in CoreData

Sono in grado di selezionare e visualizzare un’immagine dalla libreria di foto, ma il mio objective è di essere in grado di salvare l’immagine selezionata o il percorso del file nei dati di base in modo che quando viene selezionato quel record salvato viene visualizzata anche l’immagine.

Ho CoreData funzionante e sono in grado di visualizzare il testo da CoreData bene è solo l’immagine che mi tiene.

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

Passa a Elaborazione dell’immagine per scoprire come convertire UIImage in NSData (che è ciò che utilizza Core Data)

Oppure scarica da github

Impostazione dei dati principali:

Imposta due quadro: risoluzione completa e miniatura. Risoluzioni complete è per memorizzare l’immagine originale. Miniatura per memorizzare una versione più piccola da utilizzare all’interno dell’app. Ad esempio, potresti utilizzare una versione più piccola in una panoramica di UICollectionView .

Le immagini vengono memorizzate come Binary Data nei Core Data . Il tipo corrispondente in Foundation è NSData . Converti di nuovo in UIImage con UIImage(data: newImageData)

inserisci la descrizione dell'immagine qui


inserisci la descrizione dell'immagine qui


Seleziona la casella Permetti archiviazione esterna per i campi Dati binari. Ciò salverà automaticamente le immagini nel file system e le farà riferimento in Core Data

inserisci la descrizione dell'immagine qui

Connetti le due quadro, creando una relazione uno a uno tra i due.

inserisci la descrizione dell'immagine qui

Vai a Editor it seleziona Crea NSManagedObjectSubclass . Ciò genererà i file con Classi che rappresentano le sottocategorie dell’object gestito. Questi verranno visualizzati nella struttura del file di progetto.

inserisci la descrizione dell'immagine qui


Impostazione di base ViewController:

Importa il seguente:

 import UIKit import CoreData 

  • Imposta due UIButtons e un UIImageView in Interface Builder
  • Crea due code di invio, una per CoreData e una per le conversioni 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 } } } } 

Questa funzione imposta un valore su managedContext sul thread corretto. Poiché CoreData richiede che tutte le operazioni in un NSManagedObjectContext avvengano nello stesso thread.

 extension ViewController { func coreDataSetup() { dispatch_sync(saveQueue) { self.managedContext = AppDelegate().managedObjectContext } } } 

Estendi UIViewController modo che sia conforms a UIImagePickerControllerDelegate e UINavigationControllerDelegate Sono necessari per UIImagePickerController .

Creare una funzione di installazione e creare anche la funzione delegate 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 immediatamente UIImagePickerController , altrimenti l’applicazione sembrerà bloccarsi.


Elaborazione dell’immagine:

Chiama questa funzione all’interno di imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) .

  • In primo luogo ottenere la data corrente con timeIntervalSince1970 . Restituisce un valore NSTimerInterval in secondi. Questo converte piacevolmente in un Double . Servirà come un ID univoco per le immagini e come un modo per ordinarle.

  • Ora è un buon momento per passare alla coda separata e liberare la coda principale. Ho usato dispatch_async(convertQueue) per eseguire il sollevamento pesante su un thread separato.

  • Quindi è necessario convertire UIImage in NSData ciò è fatto con UIImageJPEGRepresentation(image, 1) . Il 1 rappresenta la qualità dove 1 è il più alto e 0 è il più basso. Restituisce un optional quindi ho usato il binding opzionale.

  • Ridimensiona l’immagine alla dimensione desiderata della miniatura e converti anche in NSData .

Codice:

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

Questa funzione esegue il salvataggio effettivo.

  • Vai al thread CoreData con dispatch_barrier_sync(saveQueue)
  • Innanzitutto inserire un nuovo object FullRes e un nuovo object Thumbnail nel contesto dell’object gestito.
  • Imposta i valori
  • Imposta la relazione tra FullRes e Thumbnail
  • Utilizzare do try catch per tentare un salvataggio
  • Aggiorna il contesto object gestito per liberare memoria

Usando dispatch_barrier_sync(saveQueue) siamo sicuri di poter archiviare in modo sicuro una nuova immagine e che i nuovi salvataggi o carichi attenderanno fino al termine.

Codice:

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

Per caricare un’immagine:

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

Le funzioni utilizzate per ridimensionare l’immagine:

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

Core Data non è pensato per salvare grandi file binari come immagini. Utilizzare invece Directory documenti nel file system.

Ecco un codice di esempio per raggiungerlo.

 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 }