将选取的图像保存到CoreData

我能够从照片库中挑选并显示图像,但是我的目标是能够将所选图像或文件path保存到核心数据,以便在select保存的logging时也可以显示图像。

我有CoreData的工作,我能够显示来自CoreData的文本罚款,这只是图像抱着我。

@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) 

跳到处理图像以找出如何将UIImage转换为NSData (这是Core Data使用的)

或从github下载

核心数据设置:

设置两个实体:全分辨率和缩略图。 完整解决scheme是存储原始图像。 缩略图存储在应用程序内使用较小的版本。 例如,您可以在UICollectionView概述中使用较小的版本。

图像作为Binary Data存储在Core DataFoundation对应的types是NSData 。 用UIImage(data: newImageData)转换回UIImage

在这里输入图像描述


在这里输入图像描述


检查允许外部存储框为二进制数据字段。 这将自动将图像保存在文件系统中,并在核心数据中引用它们

在这里输入图像描述

连接两个实体,在两者之间build立一对一的关系。

在这里输入图像描述

转到编辑器select创buildNSManagedObjectSubclass 。 这将生成代表您的托pipe对象子类的类文件。 这些将出现在您的项目文件结构中。

在这里输入图像描述


基本的ViewController设置:

导入以下内容:

 import UIKit import CoreData 

  • 在Interface Builder中设置两个UIButtons和一个UIImageView
  • 创build两个派遣队列,一个用于CoreData,一个用于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 } } } } 

该函数在正确的线程managedContext值设置为managedContext 。 由于CoreData需要在一个NSManagedObjectContext所有操作发生在同一个线程中。

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

扩展UIViewController ,使其符合UIImagePickerControllerDelegateUINavigationControllerDelegate这些都是UIImagePickerController所必需的。

创build一个设置函数,并创build委托函数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) } } 

立即closuresUIImagePickerController ,否则应用程序将出现冻结。


处理图像:

imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?)里调用这个函数imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?)

  • 首先用timeIntervalSince1970获取当前date。 这NSTimerInterval在几秒钟内返回一个NSTimerInterval 。 这很好地转换成双。 它将作为图像的唯一标识,并作为对它们进行分类的一种方式。

  • 现在是移动到单独队列并释放主队列的好时机。 我使用dispatch_async(convertQueue)首先做一个单独的线程繁重。

  • 然后你需要将UIImage转换为NSData这是用UIImageJPEGRepresentation(image, 1)1表示质量,其中1是最高的, 0是最低的。 它返回一个可选的,所以我用可选的绑定。

  • 将图像缩放到所需的缩略图大小,并将其转换为NSData

码:

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

这个函数做了实际的保存。

  • 使用dispatch_barrier_sync(saveQueue)转到CoreData线程
  • 首先在Managed Object Context中插入一个新的FullRes和一个新的Thumbnail对象。
  • 设置值
  • 设置FullRes和缩略图之间的关系
  • 使用do try catch尝试保存
  • 刷新托pipe对象上下文以释放内存

通过使用dispatch_barrier_sync(saveQueue)我们确信我们可以安全地存储一个新的图像,新的保存或加载将等待,直到完成。

码:

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

加载图像:

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

用于缩放图像的function:

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

核心数据并不意味着保存像图像这样的大型二进制文件。 在文件系统中使用文档目录。

这里是示例代码来实现这一点。

 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 }