将选取的图像保存到CoreData [英] Saving Picked Image to CoreData
问题描述
我可以从照片库中选择并显示图片,但我的目标是能够将选取的图片或文件路径保存到核心数据,以便在选择保存的记录时,该图片也会显示。 / p>
我有CoreData工作,我能够显示来自CoreData的文本,只有图像让我起来。
@IBAction func addPic(sender:AnyObject){
pickerController.delegate = self
pickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
// 2
self.presentViewController(pickerController,animated:true,completion:nil)
//显示图片
func imagePickerController(picker:UIImagePickerController!,didFinishPickingMediaWithInfo info:NSDictionary!){
image.image = info [UIImagePickerControllerOriginalImage] as? UIImage
self.dismissViewControllerAnimated(true,completion:nil)
跳转到处理图片以了解如何将 UIImage
转换为 NSData
(这是Core Data使用的)
或从
连接两个实体,在两者之间创建一对一的关系。
转到编辑器
strong> en选择创建 NSManagedObjectSubclass 。这将生成带有代表您的Managed Object子类的类的文件。这些将显示在您的项目文件结构中。
基本ViewController设置:
导入以下内容:
import UIKit
import CoreData
- 设置两个
UIButtons
和UIImageView
- 创建两个分派队列,一个用于CoreData,另一个用于UIImage转换
class ViewController:UIViewController {
// imageview以显示加载的图片
@IBOutlet弱var imageView:UIImageView!
//捕获/加载的图像选择器
let imagePicker = UIImagePickerController()
//调度队列
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()//图像选择器委托和设置
coreDataSetup()//设置正确线程上的moc值
}
//此函数显示imagePicker
@IBAction func capture :AnyObject){// button action
presentViewController(imagePicker,animated:true,completion:nil)
}
@IBAction func load action
loadImages {(images) - >无效
如果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
}
//当图像被挑选时,它将通过此函数返回
func imagePickerController(picker:UIImagePickerController,didFinishPickingImage image:UIImage,editingInfo:[String:AnyObject]?){
self.dismissViewControllerAnimated(true,completion:nil)
prepareImageForSaving(image)
}
}
立即关闭 UIImagePickerController
,否则应用程序将冻结。
处理图片:
code> imagePickerController(picker:UIImagePickerController,didFinishPickingImage image:UIImage,editingInfo:[String:AnyObject]?)。
-
首先使用
timeIntervalSince1970
获取当前日期。这将在几秒钟内返回NSTimerInterval
。这很好地转换为Double
。 -
现在是移动到独立队列并释放的好时机主队列。我用
dispatch_async(convertQueue)
先在一个单独的线程上执行重载。 -
需要转换
UIImage
到NSData
这是通过UIImageJPEGRepresentation(image,1) / code>。
1
表示1
是最高的,0
是最低的。 -
将图片缩放到所需的缩略图大小,并转换为
NSData
。
b $ b
extension ViewController {
func prepareImageForSaving(image:UIImage){
//使用日期作为唯一ID b $ b let date:Double = NSDate()。timeIntervalSince1970
//使用gcd分派。
dispatch_async(convertQueue){
//从UIImage创建NSData
guard let imageData = UIImageJPEGRepresentation(image,1)else {
//处理失败的转换
print(jpg error)
return
}
//缩放图像,我选择了VC的大小,因为它很容易
let thumbnail = image.scale(toSize:self.view.frame.size)
guard let thumbnailData = UIImageJPEGRepresentation(thumbnail,0.7)else {
//处理失败的转换
print( jpg error)
return
}
//发送到保存函数
self.saveImage(imageData,thumbnailData:thumbnailData,date:date)
}
}
}
- 使用
dispatch_barrier_sync(saveQueue)转到CoreData线程
- 首先在
管理对象上下文中插入新的FullRes和新的缩略图对象。 - 设置值
- 设置FullRes和缩略图之间的关系
- 使用
执行catch
尝试保存 - 刷新管理对象上下文以释放内存
使用 dispatch_barrier_sync(saveQueue)
我们确定我们可以安全地存储一个新的图像,新的保存或加载将等待,直到这完成。
代码:
扩展ViewController {
func saveImage(imageData:NSData,thumbnailData:NSData,date:Double ){
dispatch_barrier_sync(saveQueue){
//在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?缩略图else {
//在moc中处理失败的新对象
print(moc error)
return
}
//设置图像数据fullres
fullRes.imageData = imageData
//设置缩略图的图像数据
thumbnail.imageData = thumbnailData
thumbnail.id = date as NSNumber
thumbnail .fullRes = fullRes
//保存新对象
do {
try moc.save()
} catch {
fatalError保存上下文:\(error))
}
//清除moc
moc.refreshAllObjects()
}
}
}
要加载图片:
extension ViewController {
func loadImages(fetches:(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错误为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))
}
}
扩展UIImage {
func scale(toSize newSize:CGSize) - > UIImage {
//确保新大小具有正确的宽高比
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
}
}
I am able to pick and display an image from the photo library, but my goal is to be able to save that picked image or file path to core data so that when that saved record is chosen that image will display also.
I have CoreData working and I am able to display text from CoreData fine it is only the image holding me up.
@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)
Skip to Processing the Image to find out how to convert UIImage
to NSData
(which is what Core Data uses)
Or download from github
Core Data Setup:
Set up two entities :
Full Resolution and Thumbnail.
Full Resolutions is to store the original image.
Thumbnail to store a smaller version to be used inside the app.
You might use a smaller version in a UICollectionView
overview for example.
Images are stored as Binary Data
in Core Data
. The corresponding type in Foundation
is NSData
. Convert back to UIImage
with UIImage(data: newImageData)
Check the Allows External Storage box for the Binary Data fields. This will automatically save the images in the file system en reference them in Core Data
Connect the two entities, creating a one to one relationship between the two.
Go to Editor en select Create NSManagedObjectSubclass. This will generate files with Classes representing your Managed Object SubClasses. These will appear in your project file structure.
Basic ViewController Setup:
Import the following :
import UIKit
import CoreData
- Setup two
UIButtons
and anUIImageView
in the Interface Builder - Create two dispatch queues, one for CoreData and one for UIImage conversions
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
}
}
}
}
This function sets a value to managedContext
on the correct thread. Since CoreData needs all operations in one NSManagedObjectContext
to happen in the same thread.
extension ViewController {
func coreDataSetup() {
dispatch_sync(saveQueue) {
self.managedContext = AppDelegate().managedObjectContext
}
}
}
Extend the UIViewController
so it conforms to UIImagePickerControllerDelegate
and UINavigationControllerDelegate
These are needed for the UIImagePickerController
.
Create a setup function and also create the delegate function 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)
}
}
Immediately dismiss the UIImagePickerController
, else the app will appear to freeze.
Processing the Image:
Call this function inside imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?)
.
First get the current date with
timeIntervalSince1970
. This returns anNSTimerInterval
in seconds. This converts nicely to aDouble
. It will serve as a unique id for the images and as a way to sort them.Now is a good time to move to the separate queue and free up the main queue. I used
dispatch_async(convertQueue)
first to do the heavy lifting on a separate thread.Then you need to convert the
UIImage
toNSData
this is done withUIImageJPEGRepresentation(image, 1)
. The1
represents the quality where1
is the highest and0
is the lowest. It returns an optional so I used optional binding.Scale the image to a desired thumbnail size and also convert to
NSData
.
Code:
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)
}
}
}
This function does the actual saving.
- Go the the CoreData thread with
dispatch_barrier_sync(saveQueue)
- First insert a new FullRes and a new Thumbnail object into the Managed Object Context.
- Set the values
- Set the relationship between FullRes and Thumbnail
- Use
do try catch
to attempt a save - Refresh the Managed Object Context to free up memory
By using dispatch_barrier_sync(saveQueue)
we are sure that we can safely store a new image and that new saves or loads will wait until this is finished.
Code:
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()
}
}
}
To load an image :
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
}
}
}
}
The functions used to scale the image:
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
}
}
这篇关于将选取的图像保存到CoreData的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!