In my swift code the goal is to save a image into core data. My code right now is not working. Its not the right type. The code works if it is a string but trying to save it to binary data is not working. I tried creating a UIImage object and it is not working. Core data binary is called "pic"
import UIKit;import CoreData
class ViewController: UIViewController {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let co = UIImage(named: "a.png")
let entityA = NSEntityDescription.insertNewObject(forEntityName: "Info" , into: context)
entityA.setValue(co,forKey: "pic")
}
}
I haven't worked with Core Data for quite a while, but you almost certainly want to serialize your image as Data and then save it as a BLOB (Binary Large Object).
You should be able to use the UIImage pngData() function to convert it to data.
Check out this tutorial for more details.
Related
My swift code below saves 3 images. What I want to do is overwrite iamgedata2 is func press. Imagedata2 should be replaced with Gwen. So the order should be Gwen Gwen Gwen instead of Gwen gwen2 Gwen. I don't know what really to put in func press to achieve this goal.
import UIKit; import CoreData
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .orange
let gwen = UIImage(named: "blank")
let gwen2 = UIImage(named: "g.jpg")
if let imageData = gwen.self?.pngData() {
CoredataHandler.shareInstance.saveImage(data: imageData)
}
if let imageData2 = gwen2.self?.pngData() {
CoredataHandler.shareInstance.saveImage(data: imageData2)
}
if let imageData3 = gwen.self?.pngData() {
CoredataHandler.shareInstance.saveImage(data: imageData3)
}
}
#objc func press(){
CoredataHandler.shareInstance.saveImage(data: 1)
return
}
}
class CoredataHandler : NSManagedObject {
static let shareInstance = CoredataHandler()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
private class func getContext() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
func saveImage(data: Data) {
let imageInstance = Information(context: context)
imageInstance.pic = data
do {
try context.save()
} catch {
print(error.localizedDescription)
}
}
}
If you want a Core Data entity that can store more than one image, you have a few options:
Declare multiple image properties
Instead of just having a pic property, have more than one. As many as you like. Name them pic1, pic2, pic3, etc, or whatever seems best for your app. In code, read or write whichever makes sense at the time.
This is easy but not flexible, since your code can only save up to the number of attributes you declare in the model.
Use an array property with transformable
With a transformable attribute you can store any data that can be converted to a Data. In your case you'd do something like this:
Two things to notice: The type is transformable, and the custom class is [Data]. Xcode will generate code where the property type is [Data]?. You can save as many Data blobs as you want in it, representing however many images you want.
This is also easy but may use a lot of memory, because you can't access one image without loading all of them into memory at once. If you always load all of them anyway then it's no different. If you often load only one of them, this technique might use a lot more memory (e.g. 4 images would mean around 4x as much memory used).
Use a separate entity to hold the image and a to-many relationship
With this approach you'd create a new Core Data entity that only holds the image. Your existing entity would have a to-many relationship to this entity. You'd create as many image-only instances as you want, and the relationship would mean they were all available at any time.
You would probably want to make sure the to-many relationship is ordered, otherwise the images would be an unordered set that could be in any order.
This is a little more complex to write but it's flexible and doesn't have the potential memory problems of other approaches.
I have a collection view. I want to fill each cell with the images that are stored in the apps core data. However the cells only fill with the last image that was taken.
not each individual cell benign filled with each individual image.
my code is as follows:
when the user takes a photo it is compressed and stored in core data:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true)
guard let image = info[.editedImage] as? UIImage else {
print("No image found")
return
}
// print out the image size as a test
print(image.size)
//convert image to savable type for coredata
var jpegImageData = image.jpegData(compressionQuality: 1.0)
jegImage = jpegImageData!
//calling function in coredataManger to save image in data format
CoreDataManager.sharedInstance.addImage()
}
The adding to core data function that was activated above:
func addImage() {
let entityName = NSEntityDescription.entity(forEntityName: "ImageData", in: context)
let image2 = NSManagedObject(entity: entityName!, insertInto: context)
image2.setValue(jegImage, forKeyPath: "imageName")
do {
try context.save()
print("image is saved")
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
ive global variables to pass images from one VC to another
//global variables for images saved to core data
var jegImage = Data()
var theImagePulled = UIImage()
Trying to populate the collection view cell with the images stored in core data:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomeRoomCell", for: indexPath) as! HomeRoomCell
let name = cartoonArr[indexPath.item]
//setting image shown in the colleciton view equal to the gloabl images stored in core data
let image = theImagePulled
cell.bindData(name: name, image: image)
return cell
}
So the problem is that the cell gets populated but they all populate with the same image. this image is the last image that was added to core data model, even though there are several images in the core data model. I have been playing around with trying to set up an array that stores all these core data images but i can't figure it out. any ideas?
Get rid of the global variables, which are indeed capturing the most recent image.
Use NSFetchedResultsController in your collection view.
If you’re new to Core Data, you might consider passing just a string around and displaying that, first. Then move on to images.
I am able to successfully save my image in user defaults as I use my app and retrieve it just fine but am having trouble figuring out how to retrieve it in a simple via init(). In my example it will returns the AppValue.avatar (my default image) but won't return the stored image. It can't find the previously stored image so it substitutes my default image it fails because I have UIImage in the published variable. I think it must have to be retrieved as data but if I change UIImage in the init() to Data Xcode is not happy.
class UserSettings: ObservableObject {
#Published var avatar: UIImage {
didSet {
/// Convert to data using .pngData() on the image so it will store. It won't take the UIImage straight up.
let pngRepresentation = avatar.pngData()
UserDefaults.standard.set(pngRepresentation, forKey: "avatar")
printSave(forKey: "avatar", value: avatar)
}
}
init() {
self.avatar = UserDefaults.standard.object(forKey: "avatar") as? UIImage ?? AppValue.avatar
}
}
It's simply a matter of keeping mental track of what type a thing is.
You saved the image as its pngData (correctly). This is not a UIImage; it is a Data. Your pngRepresentation, which is what gets saved, is a Data.
Hence when you retrieve the image and say as? UIImage, that test fails. It is not a UIImage. It's a Data.
Therefore, fetch data(forKey:) (or say as? Data instead of as? UIImage). Now you have the Data. then call UIImage.init(data:) to retrieve the image.
I have saved an array of custom objects that conforms to NSCoding protocol in UserDefaults using NSKeyedArchiver. But when I try to retrieve it using NSKeyedUnarchiver, i get runtime errors from Xcode. I have tried initialising NSArray using unarchived data but it also failed. My guess is that while unarchiving swift doesn't understand custom elements of this array. How shall I do it?
This is how I archived array of custom objects
static func saveCategoryList(_ categoryList : [Category]!) -> Void{
let userDefaults = UserDefaults.standard
let categoryListData = NSKeyedArchiver.archivedData(withRootObject: categoryList)
userDefaults.set(categoryListData, forKey: Constants.CategoryList)
userDefaults.synchronize()
}
I get error like the attached screenshot in runtime. I am quite sure I am not doing it right. How can get my desired result?
I have a button to save picture data in core data but when I push it, it is freezing because size of the data is big. I did try to use dispatch_async but it didn’t work. How do I create the icon/indicator showing that it is loading/bookmarking rather than just freezing?
#IBAction func save() {
let content = self.foodMenu?["content"].string
let urlString = self.foodMenu?["thumbnail_images"]["full"]["url"]
let urlshare = NSURL(string: urlString!.stringValue)
let imageData = NSData(contentsOfURL: urlshare!)
let images = UIImage(data: imageData!)
dispatch_async(dispatch_get_main_queue(), {
if let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext {
self.foodClass = NSEntityDescription.insertNewObjectForEntityForName("Foods",
inManagedObjectContext: managedObjectContext) as! Foods
self.foodClass.content = content
self.foodClass.image = UIImageJPEGRepresentation(images, 1)
var e: NSError?
if managedObjectContext.save(&e) != true {
println("insert error: \(e!.localizedDescription)")
return
}
}
First, it is unlikely it is the save that is slow. I would suspect that your creation of the JPEG representation is the slow part.
Second, you are wanting to hide a problem by putting up a spinner. That really is bad for the user experience. Far better to do the following (yes it is more code);
Move your image creation and saving to a background queue.
Restructure your Core Data stack so that your saves to disk are on a private queue.
This involves using a background queue and multiple contexts in Core Data but getting this data processing off the User Interface thread is the right answer.