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)
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
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
Verbinden Sie die beiden Entitäten und erstellen Sie eine Eins-zu-Eins-Beziehung zwischen den beiden.
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.
Grundlegendes ViewController-Setup:
Importieren Sie Folgendes:
import UIKit
import CoreData
UIButtons
und eine UIImageView
im Interface Builder einclass 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.
dispatch_barrier_sync(saveQueue)
in den CoreData-Thread.do try catch
, um das Speichern zu versuchenDurch 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
}
}
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 }