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)
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)
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.
Connectez les deux entités en créant une relation un à un entre les deux.
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.
Configuration de base de ViewController:
Importez les éléments suivants:
import UIKit
import CoreData
UIButtons
et une UIImageView
dans le Générateur d'interfaceclass 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.
dispatch_barrier_sync(saveQueue)
do try catch
pour tenter une sauvegardeEn 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
}
}
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 }