Saving Selected Image in CoreData

I can select and display the image from the photo library, but my goal is to save the selected image or the path to the main data, so that when the saved record is saved, the image will also be displayed.

CoreData works for me, and I can display text from CoreData in order, this is just the image holding me.

@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) 
+22
swift core-data uiimage uiimagepickercontroller
Jan 17 '15 at 3:50
source share
2 answers

Go to Process Image to learn how to convert UIImage to NSData (which uses Core Data)

Or download from github

Master data setup:

Set up two objects: Full resolution and thumbnail. Full permissions - keep the original image. A thumbnail for storing a smaller version that will be used inside the application. For example, you can use a smaller version in a UICollectionView .

Images are saved as Binary Data in Core Data . The corresponding type in Foundation is NSData . Return to UIImage with UIImage(data: newImageData)

enter image description here




enter image description here




Select the Allow external storage for binary data fields check box. This will automatically save the images to the file system and contact them in Core Data p>

enter image description here

Connect two objects, creating a relationship between them.

enter image description here

Go to ru Editor , select Create NSManagedObjectSubclass . This will generate files with classes representing subclasses of managed objects. They will appear in your project file structure.

enter image description here




Basic installation of ViewController:

Import the following:

 import UIKit import CoreData 



  • Configure two UIButtons and UIImageView in the interface builder
  • Create two send queues, one for CoreData and one for UIImage conversions



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



This function sets the managedContext value in the correct thread. Because CoreData requires all operations in the same NSManagedObjectContext performed on the same thread.

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



Extend the UIViewController to match the UIImagePickerControllerDelegate and UINavigationControllerDelegate They are needed for the UIImagePickerController .

Create a setup function, and also create a delegate function 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) } } 

Release the UIImagePickerController , otherwise the application will be frozen.




Image processing:

Call this function inside imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) .

  • First enter the current date using timeIntervalSince1970 . This returns NSTimerInterval in seconds. It converts well to Double . It will serve as a unique identifier for images and as a way to sort them.

  • Now is the time to move to a separate queue and free the main queue. At first I used dispatch_async(convertQueue) to do a heavy lift on a separate thread.

  • Then you need to convert the UIImage to NSData , this is done using UIImageJPEGRepresentation(image, 1) . 1 represents quality, where 1 is the highest and 0 is the lowest. It returns optional, so I used optional binding.

  • Enlarge the image to the desired sketch size, and convert it to NSData .

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

This function performs actual savings.

  • Go to the CoreData stream with dispatch_barrier_sync(saveQueue)
  • First insert the new FullRes and the new Thumbnail into the Managed Object Context.
  • Set Values
  • Establish Link between FullRes and Thumbnail
  • Use do try catch to try to save
  • Refresh managed entity context to free memory

Using dispatch_barrier_sync(saveQueue) , we are confident that we can safely store the new image and that the new ones saved or uploaded will wait until it ends.

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

To upload an 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 } } } } 

Functions used to scale the 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 } } 
+65
Jan 17 '15 at 6:02
source share
— -

Master data is not intended to store large binary files such as images. Use the Document Directory on the file system instead.

Here is a sample code for this.

 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
Mar 23 '16 at 6:29
source share



All Articles