2015-01-17 8 views
23

Sono in grado di prelevare e visualizzare un'immagine dalla libreria di foto, ma il mio obiettivo è 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 mostrerà anche.Salvataggio immagine prelevata su CoreData

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

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

risposta

67

Vai al elaborazione dell'immagine per scoprire come convertire UIImage-NSData (che è quello che utilizza Core Data)

o scaricare la configurazione di dati github

Nucleo:

Impostare due entità: Risoluzione completa e miniatura. Le risoluzioni complete consentono di memorizzare l'immagine originale. Miniatura per memorizzare una versione più piccola da utilizzare all'interno dell'app. Ad esempio, è possibile utilizzare una versione ridotta in una panoramica UICollectionView.

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

enter image description here


enter image description here


Controllare il Consente External Storage box per i campi dati binari. Questo salverà automaticamente le immagini nel file system en riferimento loro in Core Data

enter image description here

Collegare le due entità, la creazione di una relazione uno a uno tra i due.

enter image description here

Vai Editor en selezionare Crea NSManagedObjectSubclass. Questo genererà file con classi che rappresentano le sottocategorie oggetto gestito. Questi verranno visualizzati nella struttura del file di progetto.

enter image description here


base ViewController Setup:

importare i seguenti:

import UIKit 
import CoreData 

  • Setup due UIButtons e un UIImageView nel Generatore Interface
  • Creare due code spedizione, uno per CoreData e uno 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 managedContext sul filo corretta . Dal momento che CoreData richiede che tutte le operazioni in uno NSManagedObjectContext avvengano nello stesso thread.

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

Estendere il UIViewController modo che soddisfi UIImagePickerControllerDelegate e UINavigationControllerDelegate Tali misure sono necessarie per il UIImagePickerController.

creare una funzione di messa a punto e di creare anche la funzione delegata 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) 

    } 
} 

immediatamente respingere il UIImagePickerController, altrimenti l'applicazione apparirà a congelare.


l'elaborazione dell'immagine:

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

  • Prima ottenere la data corrente con timeIntervalSince1970. Questo restituisce uno NSTimerInterval in pochi secondi. Questo converte bene 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 lo UIImage in NSData con UIImageJPEGRepresentation(image, 1). Lo 1 rappresenta la qualità in cui 1 è il più alto e 0 è il più basso. Restituisce un optional quindi ho usato il binding opzionale.

  • Ridimensionare l'immagine alla dimensione desiderata della miniatura e convertirla 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 fa il risparmio effettivo.

  • andare il il filo CoreData con dispatch_barrier_sync(saveQueue)
  • prima inserire una nuova FullRes e un nuovo oggetto Thumbnail nella contesto Managed Object.
  • Impostare i valori
  • Impostare il rapporto tra FullRes e Thumbnail
  • Usa do try catch per tentare un salvataggio
  • Refresh Contesto Managed Object per liberare memoria

Utilizzando dispatch_barrier_sync(saveQueue) siamo sicuri che possiamo memorizzare una nuova immagine in modo sicuro 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 
    } 

} 
+0

Grazie Uomo che ha funzionato alla grande. Puoi anche aiutarmi a convertirlo nuovamente in UIImage in modo che possa visualizzarlo. – turtle02

+0

"Così ho generato anche le anteprime e salvato quelle in un'entità correlata." Vuoi dire che salvi una miniatura all'entità e l'immagine di grandi dimensioni a un'entità correlata? In modo che quando si chiama l'entità principale, l'immagine di grandi dimensioni non viene ancora chiamata? – Dustin

+0

In realtà non importa. Sono legati l'uno all'altro. Ma è corretto. Di solito ho un'entità per l'utilizzo in app con tutti i dati che accompagnano un'immagine. Titolo dell'album, posizione, ora, miniatura, .... E un altro che contiene il res completo per l'esportazione. Questo è utile anche se si applicano i filtri. Non è necessario applicare filtri alle immagini a piena risoluzione quando l'utente non sta esportando. Lui/lei potrebbe semplicemente giocare con le impostazioni. –

0

Core Data non intende salvare grandi file binari come le immagini. Utilizzare invece Directory documenti nel file system.

Ecco un esempio di codice per ottenere ciò.

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

Non è questo ciò che consente di selezionare "Consente l'archiviazione esterna" nei campi Dati binari? – justColbs