将拾取的图像保存到CoreData

时间:2015-01-17 03:50:25

标签: swift core-data uiimage uiimagepickercontroller

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

我有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)

2 个答案:

答案 0 :(得分:92)

跳至处理图片,了解如何将UIImage转换为NSData(这是Core Data使用的)

或从github下载

核心数据设置:

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

图片在Binary Data中存储为Core DataFoundation中的相应类型为NSData。使用UIImage

转换回UIImage(data: newImageData)

enter image description here


enter image description here


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

enter image description here

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

enter image description here

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

enter image description here


基本ViewController设置:

导入以下内容:

import UIKit
import CoreData

  • 在Interface Builder
  • 中设置两个UIButtons和一个UIImageView
  • 创建两个调度队列,一个用于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。由于CoreData需要在一个NSManagedObjectContext中的所有操作都发生在同一个线程中。

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

扩展UIViewController,使其符合UIImagePickerControllerDelegateUINavigationControllerDelegate 这些是UIImagePickerController

所必需的

创建设置功能并创建委托功能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)

    }
}

立即关闭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) {

        // 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主题
  • 首先将新的FullRes和一个新的Thumbnail对象插入 托管对象上下文。
  • 设置值
  • 设置FullRes和缩略图
  • 之间的关系
  • 使用do try catch尝试保存
  • 刷新托管对象上下文以释放内存

通过使用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
            }
        }
    }
}

用于缩放图像的函数:

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
    }

}

答案 1 :(得分:0)

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

以下是实现此目的的示例代码。

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 }