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)


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

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

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.

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
}
}
Grazie Uomo che ha funzionato alla grande. Puoi anche aiutarmi a convertirlo nuovamente in UIImage in modo che possa visualizzarlo. – turtle02
"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
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. –