将选取的图像保存到 CoreData [英] Saving Picked Image to CoreData

查看:21
本文介绍了将选取的图像保存到 CoreData的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我能够从照片库中选择并显示图像,但我的目标是能够将所选择的图像或文件路径保存到核心数据,以便在选择保存的记录时,该图像也将显示.

我有 CoreData 工作,我能够很好地显示 CoreData 中的文本,只是图像支撑着我.

@IBAction func addPic(sender: AnyObject) {pickerController.delegate = selfpickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary//2self.presentViewController(pickerController,动画:true,完成:nil)//显示图像func imagePickerController(picker: UIImagePickerController!,didFinishPickingMediaWithInfo 信息: NSDictionary!){image.image = info[UIImagePickerControllerOriginalImage] as?用户界面图像self.dismissViewControllerAnimated(true, 完成: nil)

解决方案

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

或从

<小时>

<小时>

选中二进制数据字段的允许外部存储框.这将自动将图像保存在文件系统中,并在 Core Data 中引用它们

连接两个实体,在两者之间创建一对一的关系.

转到编辑器,然后选择创建NSManagedObjectSubclass.这将生成带有代表您的托管对象子类的类的文件.这些将出现在您的项目文件结构中.

<小时>

基本的视图控制器设置:

导入以下内容:

导入 UIKit导入核心数据

<小时>

  • 在界面生成器中设置两个 UIButtons 和一个 UIImageView
  • 创建两个调度队列,一个用于 CoreData,一个用于 UIImage 转换
<小时>

class ViewController: UIViewController {//imageview 显示加载的图像@IBOutlet 弱变量 imageView:UIImageView!//用于捕获/加载的图像选择器让 imagePicker = UIImagePickerController()//调度队列让 convertQueue = dispatch_queue_create("convertQueue", DISPATCH_QUEUE_CONCURRENT)让 saveQueue = dispatch_queue_create("saveQueue", DISPATCH_QUEUE_CONCURRENT)//模态var managedContext : NSManagedObjectContext?覆盖 func viewDidLoad() {super.viewDidLoad()imagePickerSetup()//图像选择器委托和设置coreDataSetup()//在右侧线程上设置 moc 的值}//这个函数显示imagePicker@IBAction func capture(sender: AnyObject) {//按钮动作presentViewController(imagePicker,动画:true,完成:nil)}@IBAction func load(sender: AnyObject) {//按钮动作loadImages {(图像)->作废如果让thumbnailData = 图像?.last?.thumbnail?.imageData {让图像 = UIImage(数据:缩略图数据)self.imageView.image = 图像}}}}

<小时>

此函数在正确的线程上为 managedContext 设置一个值.因为 CoreData 需要一个 NSManagedObjectContext 中的所有操作发生在同一个线程中.

extension ViewController {功能核心数据设置(){dispatch_sync(保存队列){self.managedContext = AppDelegate().managedObjectContext}}}

<小时>

扩展 UIViewController 使其符合 UIImagePickerControllerDelegateUINavigationControllerDelegateUIImagePickerController 需要这些.

创建一个设置函数并创建委托函数imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage,editingInfo: [String : AnyObject]?)

extension ViewController : UIImagePickerControllerDelegate, UINavigationControllerDelegate {func imagePickerSetup() {imagePicker.delegate = selfimagePicker.sourceType = UIImagePickerControllerSourceType.Camera}//当一个图像被选中"时,它将通过这个函数返回func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage,editingInfo: [String : AnyObject]?) {self.dismissViewControllerAnimated(true, 完成: nil)prepareImageForSaving(图像)}}

立即关闭 UIImagePickerController,否则应用程序似乎会冻结.

<小时>

处理图像:

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

  • 首先使用 timeIntervalSince1970 获取当前日期.这会以秒为单位返回 NSTimerInterval.这很好地转换为 Double.它将作为图像的唯一 ID 并作为对它们进行排序的一种方式.

  • 现在是移动到单独队列并释放主队列的好时机.我首先使用 dispatch_async(convertQueue) 在单独的线程上完成繁重的工作.

  • 然后您需要将UIImage 转换为NSData,这是通过UIImageJPEGRepresentation(image, 1) 完成的.1 代表质量,其中1 最高,0 最低.它返回一个可选的,所以我使用了可选的绑定.

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

代码:

extension ViewController {func prepareImageForSaving(image:UIImage) {//使用日期作为唯一标识让日期:Double = NSDate().timeIntervalSince1970//使用 gcd 调度.dispatch_async(convertQueue) {//从 UIImage 创建 NSData守卫让 imageData = UIImageJPEGRepresentation(image, 1) else {//处理失败的转换打印(jpg错误")返回}//缩放图像,我选择了VC的大小,因为它很容易让缩略图 = image.scale(toSize: self.view.frame.size)守卫让缩略图数据 = UIImageJPEGRepresentation(thumbnail, 0.7) else {//处理失败的转换打印(jpg错误")返回}//发送到保存函数self.saveImage(imageData,thumbnailData:thumbnailData,日期:日期)}}}

该函数进行实际保存.

  • 使用 dispatch_barrier_sync(saveQueue)
  • 进入 CoreData 线程
  • 首先插入一个新的 FullRes 和一个新的 Thumbnail 对象到托管对象上下文.
  • 设置值
  • 设置 FullRes 和 Thumbnail 的关系
  • 使用do try catch尝试保存
  • 刷新托管对象上下文以释放内存

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

代码:

extension ViewController {func saveImage(imageData:NSData,thumbnailData:NSData,日期:双){dispatch_barrier_sync(保存队列){//在 moc 中创建新对象守卫让 moc = self.managedContext else {返回}守卫让 fullRes = NSEntityDescription.insertNewObjectForEntityForName("FullRes", inManagedObjectContext: moc) as?FullRes,让缩略图 = NSEntityDescription.insertNewObjectForEntityForName("Thumbnail", inManagedObjectContext: moc) as?其他缩略图{//在 moc 中处理失败的新对象打印(模拟错误")返回}//设置fullres的图像数据fullRes.imageData = imageData//设置缩略图的图像数据缩略图.图像数据 = 缩略图数据缩略图.id = 日期作为 NSNumber缩略图.fullRes = fullRes//保存新对象做 {试试 moc.save()} 抓住 {致命错误(未能保存上下文:(错误)")}//清除 mocmoc.refreshAllObjects()}}}

加载图片:

extension ViewController {func loadImages(fetched:(images:[FullRes]?) -> Void) {dispatch_async(saveQueue) {守卫让 moc = self.managedContext else {返回}让 fetchRequest = NSFetchRequest(entityName: "FullRes")做 {让结果 = 尝试 moc.executeFetchRequest(fetchRequest)让 imageData = 结果为?[全分辨率]dispatch_async(dispatch_get_main_queue()){获取(图像:imageData)}} catch let error as NSError {打印(无法获取(错误),(错误.userInfo)")返回}}}}

用于缩放图像的函数:

扩展 CGSize {func resizeFill(toSize: CGSize) ->CG尺寸{让缩放: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))}}扩展 UIImage {func scale(toSize newSize:CGSize) ->用户界面{//确保新尺寸具有正确的纵横比让 aspectFill = self.size.resizeFill(newSize)UIGraphicsBeginImageContextWithOptions(aspectFill, false, 0.0);self.drawInRect(CGRectMake(0, 0, aspectFill.width, aspectFill.height))让 newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()UIGraphicsEndImageContext()返回新图片}}

I am able to pick and display an image from the photo library, but my goal is to be able to save that picked image or file path to core data so that when that saved record is chosen that image will display also.

I have CoreData working and I am able to display text from CoreData fine it is only the image holding me up.

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

解决方案

Skip to Processing the Image to find out how to convert UIImage to NSData (which is what Core Data uses)

Or download from github

Core Data Setup:

Set up two entities : Full Resolution and Thumbnail. Full Resolutions is to store the original image. Thumbnail to store a smaller version to be used inside the app. You might use a smaller version in a UICollectionView overview for example.

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



Check the Allows External Storage box for the Binary Data fields. This will automatically save the images in the file system en reference them in Core Data

Connect the two entities, creating a one to one relationship between the two.

Go to Editor en select Create NSManagedObjectSubclass. This will generate files with Classes representing your Managed Object SubClasses. These will appear in your project file structure.


Basic ViewController Setup:

Import the following :

import UIKit
import CoreData


  • Setup two UIButtons and an UIImageView in the Interface Builder
  • Create two dispatch 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 a value to managedContext on the correct thread. Since CoreData needs all operations in one NSManagedObjectContext to happen in the same thread.

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


Extend the UIViewController so it conforms to UIImagePickerControllerDelegate and UINavigationControllerDelegate These are needed for the UIImagePickerController.

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

    }
}

Immediately dismiss the UIImagePickerController, else the app will appear to freeze.


Processing the Image:

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

  • First get the current date with timeIntervalSince1970. This returns an NSTimerInterval in seconds. This converts nicely to a Double. It will serve as a unique id for the images and as a way to sort them.

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

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

  • Scale the image to a desired thumbnail size and also convert to 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)

        }
    }
}

This function does the actual saving.

  • Go the the CoreData thread with dispatch_barrier_sync(saveQueue)
  • First insert a new FullRes and a new Thumbnail object into the Managed Object Context.
  • Set the values
  • Set the relationship between FullRes and Thumbnail
  • Use do try catch to attempt a save
  • Refresh the Managed Object Context to free up memory

By using dispatch_barrier_sync(saveQueue) we are sure that we can safely store a new image and that new saves or loads will wait until this is finished.

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

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

}

这篇关于将选取的图像保存到 CoreData的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆