Сохранение выбранного изображения в CoreData

Я могу выбрать и отобразить изображение из библиотеки фотографий, но моя цель - сохранить выбранный образ или путь к основным данным, чтобы при сохранении этой сохраненной записи отображалось и изображение.

У меня работает 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)

Ответ 1

Перейдите к Обработать изображение, чтобы узнать, как преобразовать UIImage в NSData (что использует Core Data)

Или скачать из github

Настройка основных данных:

Настройте два объекта: Полное разрешение и миниатюра. Полные разрешения - сохранить исходное изображение. Миниатюра для хранения меньшей версии, которая будет использоваться внутри приложения. Например, вы можете использовать меньшую версию в обзоре UICollectionView.

Изображения сохраняются как Binary Data в Core Data. Соответствующий тип в Foundation равен NSData. Вернитесь к UIImage с помощью UIImage(data: newImageData)

введите описание изображения здесь


введите описание изображения здесь


Установите флажок Разрешить внешнее хранилище для полей двоичных данных. Это автоматически сохранит изображения в файловой системе и свяжется с ними в Core Data​​p >

введите описание изображения здесь

Подключите два объекта, создавая отношения между ними между собой.

введите описание изображения здесь

Перейдите в Редактор ru, выберите Create NSManagedObjectSubclass. Это будет генерировать файлы с классами, представляющими подклассы управляемых объектов. Они появятся в вашей структуре файла проекта.

введите описание изображения здесь


Базовая установка ViewController:

Импортируйте следующее:

import UIKit
import CoreData

  • Настройте два 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, чтобы он соответствовал UIImagePickerControllerDelegate и UINavigationControllerDelegate Они необходимы для 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. Он будет служить уникальным идентификатором для изображений и как способ их сортировки.

  • Теперь самое время переместиться в отдельную очередь и освободить основную очередь. Сначала я использовал 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)

        }
    }
}

Эта функция выполняет фактическую экономию.

  • Перейдите в поток CoreData с помощью dispatch_barrier_sync(saveQueue)
  • Сначала вставьте новый FullRes и новый объект Thumbnail в Контекст управляемого объекта.
  • Задайте значения
  • Установить связь между FullRes и Thumbnail
  • Используйте 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
    }

}

Ответ 2

Основные данные не предназначены для сохранения больших двоичных файлов, таких как изображения. Вместо этого используйте Document Directory в файловой системе.

Вот пример кода для этого.

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 }