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)
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)
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
Połącz dwa byty, tworząc między nimi relację jeden do jednego.
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.
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
. ZwracaNSTimerInterval
w sekundach. To ładnie przekształca się wDouble
. 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
naNSData
odbywa się to za pomocąUIImageJPEGRepresentation(image, 1)
.1
reprezentuje jakość, gdzie1
jest najwyższa i0
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
}
}
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 }
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