将选取的图像保存到 CoreData
Saving Picked Image to 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)
跳至处理图像了解如何将UIImage
转换为NSData
(这是Core Data使用的)
或从github
下载
核心数据设置:
设置两个实体:
全分辨率和缩略图。
Full Resolutions 是存储原始图像。
用于存储要在应用程序内部使用的较小版本的缩略图。
例如,您可以在 UICollectionView
概述中使用较小的版本。
图像在 Core Data
中存储为 Binary Data
。 Foundation
中对应的类型是NSData
。使用 UIImage(data: newImageData)
转换回 UIImage
选中二进制数据字段的允许外部存储框。这将自动将图像保存在文件系统中并在 Core Data
中引用它们
连接两个实体,在两者之间创建一对一的关系。
转到 Editor en select 创建 NSManagedObjectSubclass。
这将生成带有 类 的文件,代表您的托管对象 Sub类。这些将出现在您的项目文件结构中。
基本 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
使其符合 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
获取当前日期。这 returns 秒 NSTimerInterval
。这很好地转换为 Double
。它将作为图像的唯一 ID 和对图像进行排序的方式。
现在是移动到独立队列并释放主队列的好时机。我首先使用 dispatch_async(convertQueue)
在单独的线程上完成繁重的工作。
然后你需要将UIImage
转换成NSData
,这是用UIImageJPEGRepresentation(image, 1)
完成的。 1
表示质量,其中 1
最高,0
最低。它 returns 是可选的,所以我使用了可选绑定。
将图像缩放到所需的缩略图大小并转换为 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和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
}
}
Core Data 并不是为了保存像图像这样的大二进制文件。请改用文件系统中的文档目录。
这是实现该目的的示例代码。
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 }
我可以从照片库中选择并显示图像,但我的目标是能够将选择的图像或文件路径保存到核心数据,以便在选择保存的记录时也显示该图像.
我有 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)
跳至处理图像了解如何将UIImage
转换为NSData
(这是Core Data使用的)
或从github
下载核心数据设置:
设置两个实体:
全分辨率和缩略图。
Full Resolutions 是存储原始图像。
用于存储要在应用程序内部使用的较小版本的缩略图。
例如,您可以在 UICollectionView
概述中使用较小的版本。
图像在 Core Data
中存储为 Binary Data
。 Foundation
中对应的类型是NSData
。使用 UIImage(data: newImageData)
UIImage
选中二进制数据字段的允许外部存储框。这将自动将图像保存在文件系统中并在 Core Data
中引用它们连接两个实体,在两者之间创建一对一的关系。
转到 Editor en select 创建 NSManagedObjectSubclass。 这将生成带有 类 的文件,代表您的托管对象 Sub类。这些将出现在您的项目文件结构中。
基本 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
使其符合 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
获取当前日期。这 returns 秒NSTimerInterval
。这很好地转换为Double
。它将作为图像的唯一 ID 和对图像进行排序的方式。现在是移动到独立队列并释放主队列的好时机。我首先使用
dispatch_async(convertQueue)
在单独的线程上完成繁重的工作。然后你需要将
UIImage
转换成NSData
,这是用UIImageJPEGRepresentation(image, 1)
完成的。1
表示质量,其中1
最高,0
最低。它 returns 是可选的,所以我使用了可选绑定。将图像缩放到所需的缩略图大小并转换为
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和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
}
}
Core Data 并不是为了保存像图像这样的大二进制文件。请改用文件系统中的文档目录。
这是实现该目的的示例代码。
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 }