webentwicklung-frage-antwort-db.com.de

Das ausgewählte Bild wird in CoreData gespeichert

Ich kann ein Bild aus der Fotobibliothek auswählen und anzeigen. Mein Ziel ist es jedoch, das ausgewählte Bild oder den Dateipfad in den Kerndaten zu speichern, sodass das Bild bei der Auswahl des gespeicherten Datensatzes ebenfalls angezeigt wird.

Ich arbeite mit CoreData und ich kann Text aus CoreData anzeigen. Es ist nur das Bild, das mich hochhält.

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

Fahren Sie mit Processing the Image fort, um herauszufinden, wie Sie UIImage in NSData konvertieren (was Core Data verwendet).

Oder von github herunterladen

Core Data Setup:

Richten Sie zwei Entitäten ein: Full Resolution und Thumbnail . Full Resolutions: Speichern Sie das Originalbild . Thumbnail, um eine kleinere Version zu speichern, die in der App verwendet werden soll . Möglicherweise verwenden Sie eine kleinere Version in einer UICollectionView-Übersicht zum Beispiel.

Bilder werden als Binary Data in Core Data gespeichert. Der entsprechende Typ in Foundation ist NSData. Mit UIImage(data: newImageData) zurück in UIImage konvertieren

 enter image description here


 enter image description here


Aktivieren Sie das Kontrollkästchen Erlaubt externen Speicher für die Felder für binäre Daten. Dadurch werden die Bilder automatisch im Dateisystem gespeichert und in Core Data referenziert

 enter image description here

Verbinden Sie die beiden Entitäten und erstellen Sie eine Eins-zu-Eins-Beziehung zwischen den beiden. 

 enter image description here

Gehen Sie zu Editor und wählen Sie Create NSManagedObjectSubclass ..__ aus. Dadurch werden Dateien mit Klassen generiert, die Ihre verwalteten Objekt-Unterklassen darstellen. Diese werden in Ihrer Projektdateistruktur angezeigt.

 enter image description here


Grundlegendes ViewController-Setup:

Importieren Sie Folgendes:

import UIKit
import CoreData

  • Richten Sie zwei UIButtons und eine UIImageView im Interface Builder ein
  • Erstellen Sie zwei Versandwarteschlangen, eine für CoreData und eine für UIImage-Konvertierungen

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

Diese Funktion setzt einen Wert auf managedContext im richtigen Thread. Da CoreData alle Operationen in einer NSManagedObjectContext benötigt, um im selben Thread zu laufen.

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

Erweitern Sie die Variable UIViewController so, dass sie der Variablen UIImagePickerControllerDelegate und UINavigationControllerDelegate entspricht. Diese werden für die Variable UIImagePickerController benötigt.

Erstellen Sie eine Setup-Funktion und erstellen Sie auch die Delegatfunktion 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)

    }
}

Verwerfen Sie die UIImagePickerController sofort, andernfalls scheint die App einzufrieren.


Bearbeiten des Bildes:

Rufen Sie diese Funktion in imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) auf. 

  • Holen Sie sich zuerst das aktuelle Datum mit timeIntervalSince1970. Dies gibt eine NSTimerInterval in Sekunden zurück. Dies konvertiert gut in eine Double. Es wird als eindeutige ID für die Bilder dienen und sie sortieren.

  • Jetzt ist ein guter Zeitpunkt, um in die separate Warteschlange zu wechseln und die Hauptwarteschlange freizugeben. Ich habe zuerst dispatch_async(convertQueue) verwendet, um das schwere Anheben an einem separaten Thread durchzuführen.

  • Dann müssen Sie die UIImage in NSData konvertieren, dies erfolgt mit UIImageJPEGRepresentation(image, 1). Der 1 steht für die Qualität, bei der 1 die höchste und 0 die niedrigste ist. Es wird eine optionale zurückgegeben, daher habe ich die optionale Bindung verwendet.

  • Skalieren Sie das Bild auf die gewünschte Größe der Miniaturansicht und konvertieren Sie es auch in NSData.

Code:

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)

        }
    }
}

Diese Funktion führt das eigentliche Speichern aus.

  • Gehen Sie mit dispatch_barrier_sync(saveQueue) in den CoreData-Thread.
  • Fügen Sie zunächst ein neues FullRes-Objekt und ein neues Thumbnail-Objekt in den Managed Object Context ein.
  • Stellen Sie die Werte ein
  • Legen Sie die Beziehung zwischen FullRes und Thumbnail fest
  • Verwenden Sie do try catch, um das Speichern zu versuchen
  • Aktualisieren Sie den verwalteten Objektkontext, um Speicherplatz freizugeben

Durch die Verwendung von dispatch_barrier_sync(saveQueue) sind wir sicher, dass wir ein neues Image sicher speichern können und neue Speicher- oder Ladevorgänge warten, bis dieses abgeschlossen ist.

Code:

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

So laden Sie ein Bild:

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

Die zum Skalieren des Bildes verwendeten Funktionen:

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
    }

}
85
R Menke

Core Data ist nicht dazu gedacht, große Binärdateien wie Bilder zu speichern. Verwenden Sie stattdessen das Dokumentverzeichnis im Dateisystem.

Hier ist Beispielcode, um das zu erreichen.

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