Zapisywanie wybranego obrazu do CoreData

Jestem w stanie wybrać i wyświetlić obraz z biblioteki zdjęć, ale moim celem jest zapisanie wybranego obrazu lub ścieżki pliku do podstawowych danych, tak aby po wybraniu zapisanego rekordu obraz również się wyświetlał.

Mam CoreData działa i jestem w stanie wyświetlić tekst z CoreData dobrze to tylko obraz trzyma mnie w górze.

@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)
Author: R Menke, 2015-01-17

2 answers

Przejdź do przetwarzanie obrazu, aby dowiedzieć się, jak przekonwertować UIImage na NSData (czego używa Core Data)

Lub Pobierz z github

Konfiguracja Danych Podstawowych:

Utworzenie dwóch podmiotów : Pełna rozdzielczość i Miniaturka. Pełna rozdzielczość to Przechowywanie oryginalnego obrazu. Miniaturka do przechowywania mniejszej wersji, która ma być używana wewnątrz aplikacji. Możesz na przykład użyć mniejszej wersji w przeglądzie UICollectionView.

Obrazy są przechowywane jako Binary Data w Core Data. Odpowiedni typ w Foundation to NSData. Convert back to UIImage with UIImage(data: newImageData)

Tutaj wpisz opis obrazka


Tutaj wpisz opis obrazka


Zaznacz pole pozwala na Zewnętrzne przechowywanie dla pól danych binarnych. Spowoduje to automatyczne zapisanie obrazów w systemie plików en odwoływanie się do nich w Core Data

Tutaj wpisz opis obrazka

Połącz dwa byty, tworząc między nimi relację jeden do jednego.

Tutaj wpisz opis obrazka

Przejdź do Editor PL wybierz Create NSManagedObjectSubclass. Spowoduje to wygenerowanie plików z klasami reprezentującymi podklasy zarządzanych obiektów. Pojawią się one w strukturze plików projektu.

Tutaj wpisz opis obrazka


Podstawowe Ustawienia Viewcontrollera:

Importuj:

import UIKit
import CoreData

  • konfiguracja dwóch UIButtons i {[18] } w Konstruktorze interfejsu
  • Utwórz dwie kolejki wysyłkowe, jedna dla CoreData i jedna dla konwersji 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
            }
        }
    }
}

Ta funkcja ustawia wartość na managedContext w odpowiednim wątku. Ponieważ CoreData potrzebuje wszystkich operacji w jednym NSManagedObjectContext, aby miały miejsce w tym samym wątku.

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

Rozszerz UIViewController tak, aby odpowiadała UIImagePickerControllerDelegate i UINavigationControllerDelegate Są one potrzebne do UIImagePickerController.

Utwórz funkcję setup, a także Utwórz funkcję delegata 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)

    }
}

Natychmiast odrzucić UIImagePickerController, w przeciwnym razie aplikacja zostanie zamrożona.


Przetwarzanie obrazu:

Wywołanie tej funkcji wewnątrz imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?).

  • Najpierw uzyskaj aktualną datę za pomocą timeIntervalSince1970. Zwraca NSTimerInterval w sekundach. To ładnie przekształca się w Double. Będzie służył jako unikalny identyfikator dla obrazów i jako sposób na ich sortowanie.

  • Teraz jest dobry moment, aby przejść do osobnej kolejki i zwolnić kolejkę główną. Użyłem dispatch_async(convertQueue) najpierw do ciężkich podnoszenie na oddzielnym wątku.

  • Następnie musisz przekonwertować UIImage na NSData odbywa się to za pomocą UIImageJPEGRepresentation(image, 1). 1 reprezentuje jakość, gdzie 1 jest najwyższa i 0 jest najniższa. Zwraca opcjonalne, więc użyłem opcjonalnego wiązania.

  • Skaluj obraz do żądanego rozmiaru miniatury, a także przekonwertuj na NSData.

Kod:

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)

        }
    }
}

Ta funkcja dokonuje rzeczywistego zapisu.

  • Go the CoreData thread with dispatch_barrier_sync(saveQueue)
  • najpierw wstaw nowy FullRes i nowy obiekt miniaturki do Zarządzany Kontekst Obiektu.
  • Ustaw wartości
  • Ustawia relację między FullRes i miniaturką
  • użyj do try catch aby spróbować zapisać
  • odśwież kontekst zarządzanego obiektu, aby zwolnić pamięć

Używając dispatch_barrier_sync(saveQueue) jesteśmy pewni, że możemy bezpiecznie przechowywać nowy obraz i że nowe zapisywanie lub ładowanie będzie czekać, aż to skończone.

Kod:

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

Aby załadować obraz:

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

Funkcje Używane do skalowania obrazu:

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
    }

}
 80
Author: R Menke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-24 20:39:41

Podstawowe Dane nie są przeznaczone do zapisywania dużych plików binarnych, takich jak obrazy. Zamiast tego użyj katalogu dokumentu w systemie plików.

Oto przykładowy kod, aby to osiągnąć.

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
Author: Himanshu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-23 07:14:28