web-dev-qa-db-fra.com

Enregistrement de l'image sélectionnée dans CoreData

Je suis en mesure de sélectionner et d'afficher une image de la photothèque, mais mon objectif est de pouvoir enregistrer l'image sélectionnée ou le chemin d'accès au fichier dans les données principales de sorte que, lorsque cet enregistrement est sélectionné, cette image s'affiche également.

Je travaille avec CoreData et je suis capable d’afficher du texte de CoreData, c’est bien l’image qui me tient debout.

@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

Passez à Traitement de l'image pour savoir comment convertir UIImage en NSData (qui est utilisé par Core Data).

Ou téléchargez depuis github

Configuration des données de base:

Configurez deux entités: Résolution complète et miniature . Résolution complète consiste à stocker l'image d'origine . Vignette pour stocker une version plus petite à utiliser dans l'application .Vous pouvez utiliser une version plus petite. dans un aperçu UICollectionView par exemple.

Les images sont stockées en tant que Binary Data dans Core Data. Le type correspondant dans Foundation est NSData. Reconvertir en UIImage avec UIImage(data: newImageData)

 enter image description here


 enter image description here


Cochez la case Autorise le stockage externe pour les champs de données binaires. Cela enregistrera automatiquement les images dans le système de fichiers et les référencera dans Core Data.

 enter image description here

Connectez les deux entités en créant une relation un à un entre les deux. 

 enter image description here

Allez à Editor et sélectionnez Create NSManagedObjectSubclass . Cela générera des fichiers avec des classes représentant vos sous-classes d’objets gérés. Ceux-ci apparaîtront dans la structure de votre fichier de projet.

 enter image description here


Configuration de base de ViewController:

Importez les éléments suivants:

import UIKit
import CoreData

  • Configurez deux UIButtons et une UIImageView dans le Générateur d'interface
  • Créez deux files d'attente de dispatch, une pour les conversions CoreData et une pour 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
            }
        }
    }
}

Cette fonction définit une valeur sur managedContext sur le bon thread. Étant donné que CoreData a besoin de toutes les opérations d'une NSManagedObjectContext pour se produire dans le même thread.

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

Étendez la UIViewController pour qu'elle soit conforme à UIImagePickerControllerDelegate et UINavigationControllerDelegate Celles-ci sont nécessaires pour la UIImagePickerController.

Créez une fonction de configuration et créez également la fonction de délégué 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)

    }
}

Immédiatement rejeter la UIImagePickerController, sinon l'application va geler.


Traitement de l'image:

Appelez cette fonction dans imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?)

  • Commencez par obtenir la date actuelle avec timeIntervalSince1970. Cela retourne une NSTimerInterval en secondes. Ceci convertit bien en une Double. Il servira d'identifiant unique pour les images et de moyen de les trier.

  • Il est maintenant temps de passer à la file d'attente séparée et de libérer la file d'attente principale. J'ai d'abord utilisé dispatch_async(convertQueue) pour soulever des objets lourds sur un fil séparé.

  • Ensuite, vous devez convertir la UIImage en NSData. Ceci est fait avec UIImageJPEGRepresentation(image, 1). Le 1 représente la qualité où 1 est le plus élevé et 0 est le plus bas. Il retourne un optionnel alors j'ai utilisé une liaison optionnelle.

  • Ajustez l'image à la taille souhaitée et convertissez-la également en 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)

        }
    }
}

Cette fonction effectue la sauvegarde réelle.

  • Allez dans le thread CoreData avec dispatch_barrier_sync(saveQueue)
  • Insérez d’abord un nouveau FullRes et un nouvel objet Thumbnail dans le contexte Objet géré.
  • Définir les valeurs
  • Définir la relation entre FullRes et Thumbnail
  • Utilisez do try catch pour tenter une sauvegarde
  • Actualiser le contexte d'objet géré pour libérer de la mémoire

En utilisant dispatch_barrier_sync(saveQueue), nous sommes sûrs de pouvoir stocker en toute sécurité une nouvelle image et que les nouveaux sauvegardes ou chargements attendront que l'opération soit terminée.

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

Pour charger une image:

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

Les fonctions utilisées pour redimensionner l'image:

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

Les données de base ne sont pas conçues pour sauvegarder de gros fichiers binaires comme des images. Utilisez plutôt Répertoire de documents dans le système de fichiers.

Voici un exemple de code pour y parvenir.

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