Image on TableView which is retrieved from CoreData - objective-c

I want to display an image that is being retrieved from core data and every single cell will have a different image. I have done this but I do not know how to retrieve from this path and place the image into the cell. Anybody can help me on this? Thanks in advance.
NSString * filePath = #"01.jpg";
NSData * imageData = UIImageJPEGRepresentation([UIImage imageNamed:filePath], 0.1);
[productInfo setValue:imageData forKey:#"productImage"];

You could fetch the managed object from your model and place it in the array where the array would contain the photo names as below. I am assuming that your entity be photoStore and the column for storing the photo file name be nameOfPhoto, so the code would look like,
NSFetchRequest *fetchRequest=[[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entity=[NSEntityDesription entityForName:#"photoStore" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity : entity];
NSArray fecthedObject = [fetchRequest executeFetchRequest:fetchRequest error:nil];
for(NSManagedOect *photos in fetchedObect)
{
[self.arrayOfImageNames addObject : [photos valueForKey :#nameOfPhoto"]];
}
Now, inside the cellForRowAtIndexPath use the image name to add the image to your cell's imageview as,
NSString *imagePath = [NSHomeDirectory() stringByAppendingString : [[self.arrayOfImageNames objectAtIndex:indexpath.row] stringByAppendingPathComponent:"jpg"]];
cell.imageView.image=[UIImage imageNamed:imagePath];
This is how you would add the store the image name into core data and then retrieve the name construct a path to it and display it.

if you want to get path use this -
[[NSBundle mainBundle] pathForResource:#"01" ofType:#"jpg"];

Related

NSDictionary only returns one entry

I am trying to access all of my NSDictionary values after call a method that sets my dictionary items. For some reason after I invoke the method and try to log out all of my items, it only returns one item and it's also the last item in the dictionary.
Here's where I set the dictionary:
-(void)setDestinationDictionary {
destinationFileDict = [[NSMutableDictionary alloc] init];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *DestinationFiles = [fileManager contentsOfDirectoryAtPath:destinationPath error:nil];
for (NSString *file in DestinationFiles) {
NSString *sourceFile = [[destinationPath stringByAppendingPathComponent:file] lastPathComponent];
NSImage *image = [NSImage imageNamed:sourceFile];
CGFloat imageWidth = [image size].width;
CGFloat imageHeight = [image size].height;
[destinationFileDict setObject:sourceFile forKey:#"fileName"];
[destinationFileDict setObject:[NSNumber numberWithFloat:imageWidth] forKey:#"imageWidth"];
[destinationFileDict setObject:[NSNumber numberWithFloat:imageHeight] forKey:#"imageHeight"];
}
Here's where I try to call the method and log the contents of the dictionary:
-(void)initiateCopy {
[self setDestinationDictionary];
NSLog(#"%#", destinationFileDict);
}
There should be a total of 18 entries in the dictionary, but I'm only returning one and it's the last record.
Your dictionary contains three keys: fileName, imageWidth, and imageHeight.
In setDestinationDictionary, on each pass through the for loop, you set the same three keys in the same dictionary. Each pass replaces the values you stored on the prior pass. Thus at the end of setDestinationDictionary, the dictionary still contains only three keys, and the values are the values set in the last pass through the loop.
Perhaps you want to create an array of dictionaries, one for each destination file. Or perhaps you want to use the filename as the key and store the size as the value (in which case, look at +[NSValue valueWithSize:] and -[NSValue sizeValue]).

How to write items to a directory and call them, Objective c

Hi I have a bunch of photos that I download from the web and I want to store them in a directory so I dont have to reload photos already downloaded, I am having trouble understanding how exactly to do this and store the photos and reload them from the directory and such. This is what I have so far. Could someone please explain the steps involved and how to do it? Thanks
-(void) savePhotoInCache: (NSData*) photoToSave{
//I dont know if u need bundle ID?
// NSString * bundleID = [[NSBundle mainBundle] bundleIdentifier];
NSFileManager *fm = [[NSFileManager alloc] init];
NSArray * directoryPaths = [fm URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask];
NSLog(#"%#", directoryPaths);
NSURL* dirPath = nil;
//Does this create a file in my cache Directory to store my photos?
dirPath = [[directoryPaths objectAtIndex:0] URLByAppendingPathComponent:[NSString stringWithFormat:#"photos.jpg"]];
NSError* theError = nil;
[fm createDirectoryAtURL:dirPath withIntermediateDirectories:YES attributes:nil error:&theError];
// Saves the photo to the file?
[photoToSave writeToURL: dirPath atomically:NO];
NSLog(#"%#", dirPath);
//I get a deprecated warning, new version needs encoding, but I did not specify encoding in writeToURL so what do I use?
NSString * contents = [NSString stringWithContentsOfURL:dirPath ];
//After this how to I access my files and check what the contents of the file are? also, how do I limit the amount of information it stores? thanks
}
check this to how to get saved contents like this:
NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dirPath error:nil];
Now filter contains like this:
NSPredicate *fltr = [NSPredicate predicateWithFormat:#"self ENDSWITH '.jpg'"];
NSPredicate *fltr1 = [NSPredicate predicateWithFormat:#"self ENDSWITH '.png'"];
NSArray *onlyJPGs = [dirContents filteredArrayUsingPredicate:fltr];
NSArray *onlyPNGs = [dirContents filteredArrayUsingPredicate:fltr1];
To get all image content
for(NSString *fileName in dirContents)
{
NSString *imgPath = [dirPath stringByAppendingFormat:fileName];
UIImage *img = [UIImage imageWithContentsOfFile:imgPath];
//You have image use it where u want
}
If u have many images then use lazy loading of images
You are not required to handle everything by yourself. I would rather use EGO Image Loader which does pretty everything for you. it saves (cached) images in application bundle directory and reuse them whenever you need it. As far as study the classes, it uses the image url as primary key (to retrieve the image from cache).
Its pretty fast and Asynchronous.
Just add it to your project and call it like this:(This is ARC, so I don't use -release)
UIImageView *imageView = [[EGOImageView alloc] initWithPlaceholderImage:[UIImage imageNamed:#"placeholder.png"]];
imageView.frame = CGRectMake(0.0f, 44.0f, 320.0f,54.0f);
imageView.imageURL = [NSURL URLWithString:#"www.example.com/1.jpg"] ;
[self.view addSubview:imageView];
I hope it's useful.

Displaying images in uiwebview from core data record

So I have an app I've written for the iPad, and I'd like to be able to allow users to insert images into their documents by selecting an image from an album or the camera. All that works great. Because the user might keep the document longer than they keep the image in an album, I make a copy of it, scale it down a bit, and store it in a core data table that is just used for this purpose.
I store the image like this:
NSManagedObjectContext* moc=[(ActionNote3AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSString* imageName=[NSString stringWithFormat:#"img%lf.png",[NSDate timeIntervalSinceReferenceDate]];
Image* anImage = [NSEntityDescription insertNewObjectForEntityForName:#"Image" inManagedObjectContext:moc];
anImage.imageName=imageName;
anImage.imageData=UIImagePNGRepresentation(theImage);
NSError* error=nil;
if(![moc save:&error]) {...
I sub-class NSURLCache, as suggested on Cocoa With Love, and ovverride cachedResponseForRequest thusly:
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
NSString *pathString = [[[request URL] absoluteString]lastPathComponent];
NSData* data = [Image dataForImage:pathString];
if (!data) {
return [super cachedResponseForRequest:request];
}
NSURLResponse *response =[[[NSURLResponse alloc]
initWithURL:[request URL]
MIMEType:[NSString stringWithString:#"image/png"]
expectedContentLength:[data length]
textEncodingName:nil]
autorelease];
NSCachedURLResponse* cachedResponse =[[[NSCachedURLResponse alloc] initWithResponse:response data:data] autorelease];
return cachedResponse;
}
I also make sure the app uses the sub-classed NSURLCache by doing this in my app delegate in didFinishLaunchingWithOptions:
ANNSUrlCache* uCache=[[ANNSUrlCache alloc]init];
[NSURLCache setSharedURLCache:uCache];
The method that returns the image data from the core data record looks like this:
+(NSData*)dataForImage:(NSString *)name {
NSData* retval=nil;
NSManagedObjectContext* moc=[(ActionNote3AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Image" inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"imageName==%#", name];
[request setPredicate:predicate];
NSError* error=nil;
NSArray *array = [moc executeFetchRequest:request error:&error];
if ([array count]>0) {
retval=((Image*)[array objectAtIndex:0]).imageData;
}
return retval;
}
To insert the image into the web view, I have an html img tag where the name in src="" relates back to the name in the image table. The point of the NSURLCache code above is to watch for a name we have stored in the image table, intercept it, and send the actual image data for the image requested.
When I run this, I see the image getting requested in my sub-classed NSURLCache object. It is finding the right record, and returning the data as it should. However, I'm still getting the image not found icon in my uiwebview:
So Marcus (below) suggested that I not store the image data in a core data table. So I made changes to accomodate for that:
Storing the image:
NSString* iName=[NSString stringWithFormat:#"img%lf.png",[NSDate timeIntervalSinceReferenceDate]];
NSData* iData=UIImagePNGRepresentation(theImage);
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
NSString* fullPathToFile = [documentsDirectory stringByAppendingPathComponent:iName];
[iData writeToFile:fullPathToFile atomically:NO];
Retrieving the image:
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
NSString *pathString = [[[request URL] absoluteString]lastPathComponent];
NSString* iPath = [Image pathForImage:pathString];
if (!iPath) {
return [super cachedResponseForRequest:request];
}
NSData* idata=[NSData dataWithContentsOfFile:iPath];
NSURLResponse *response =[[[NSURLResponse alloc]
initWithURL:[request URL]
MIMEType:#"image/png"
expectedContentLength:[idata length]
textEncodingName:nil]
autorelease];
NSCachedURLResponse* cachedResponse =[[[NSCachedURLResponse alloc] initWithResponse:response data:idata] autorelease];
return cachedResponse;
}
In debug mode, I see that idata does get loaded with the proper image data.
And I still get the image-not-found image! Obviously, I'm doing something wrong here. I just dont know what it is.
So... What am I doing wrong here? How can I get this to work properly?
Thank you.
I would strongly suggest that you do not store the binary data in Core Data. Storing binary data in Core Data, especially on an iOS device, causes severe performance issues with the cache.
The preferred way would be to store the actual binary data on disk in a file and have a reference to the file stored within Core Data. From there it is a simple matter to change the image url to point at the local file instead.
So it turns out I was way overthinking this. When I write the HTML, I just write the path to the image in with the image tag. Works like a charm.
I would love to know why the solution I posed in my question did not work, though.
And, I did wind up not storing the images in a table.

Write to file not working

I'm trying to combine images in my app into one file and write it to disk.
NSMutableArray *array = [NSMutableArray arrayWithObjects:
[NSData dataWithContentsOfFile:#"0.png"],
[NSData dataWithContentsOfFile:#"1.png"],
[NSData dataWithContentsOfFile:#"2.png"],
nil];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
NSError *error = nil;
NSString *path=#"/Users/myusername/Desktop/_stuff.dat";
[data writeToFile:path options:NSDataWritingAtomic error:&error];
or
NSArray *array = [NSArray arrayWithObjects:
[NSImage imageNamed:#"0"],
[NSImage imageNamed:#"1"],
[NSImage imageNamed:#"2"],
nil];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
NSError *error = nil;
NSString *path=#"/Users/myusername/Desktop/_stuff.dat";
[data writeToFile:path options:NSDataWritingAtomic error:&error];
But both produce a file that is 4KB (empty). If I NSLog the error it is (null). Am I making the data the wrong way?
Edit: If I open the resulting file with a text editor, it looks like this:
I wrote a quick example:
Missing: memory management / error handling / proper file handling
// Archive
NSMutableArray *array = [[NSMutableArray alloc] init];
NSString * input = #"/Users/Anne/Desktop/1.png";
[array addObject:[NSData dataWithContentsOfFile:input]];
[array addObject:[NSData dataWithContentsOfFile:input]];
[array addObject:[NSData dataWithContentsOfFile:input]];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
NSString *path = #"/Users/Anne/Desktop/archive.dat";
[data writeToFile:path options:NSDataWritingAtomic error:nil];
// Unarchive
NSMutableArray *archive = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSData * firstObject = [archive objectAtIndex:0];
NSString * output = #"/Users/Anne/Desktop/2.png";
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:output];
[firstObject writeToURL:fileURL atomically:YES];
You can also add NSImages to the NSMutableArray:
NSString * input = #"/Users/Anne/Desktop/1.png";
NSImage *image = [[NSImage alloc] initWithContentsOfFile: input];
[array addObject:image];
But that will significantly increase the file size.
Response to the following comment:
So if I only need to access an image at runtime (in the archive), is there a way to access that image at an index without unarchiving the whole thing? Seems like unnecessary overhead to me.
I assume you're still struggling with this problem?
Hiding (or encrypting) app resources?
Like i mentioned earlier, combining all files into one big file does the trick.
Just make sure you remember the file-length of each file and file-order.
Then you can extract any specific file you like without reading the whole file.
This might be a more sufficient way if you only need to extract one file at the time.
Quick 'dirty' sample:
// Two sample files
NSData *fileOne = [NSData dataWithContentsOfFile:#"/Users/Anne/Desktop/1.png"];
NSData *fileTwo = [NSData dataWithContentsOfFile:#"/Users/Anne/Desktop/2.png"];
// Get file length
int fileOneLength = [fileOne length];
int fileTwoLength = [fileTwo length];
// Combine files into one container
NSMutableData * container = [[NSMutableData alloc] init];
[container appendData:fileOne];
[container appendData:fileTwo];
// Write container to disk
[container writeToFile:#"/Users/Anne/Desktop/container.data" atomically:YES];
// Read data and extract sample files again
NSData *containerFile = [NSData dataWithContentsOfFile:#"/Users/Anne/Desktop/container.data"];
NSData *containerFileOne =[containerFile subdataWithRange:NSMakeRange(0, fileOneLength)];
NSData *containerFileTwo =[containerFile subdataWithRange:NSMakeRange(fileOneLength, fileTwoLength)];
// Write extracted files to disk (will be exactly the same)
[containerFileOne writeToFile:#"/Users/Anne/Desktop/1_extracted.png" atomically:YES];
[containerFileTwo writeToFile:#"/Users/Anne/Desktop/2_extracted.png" atomically:YES];
// Only extract one file from the container
NSString * containerPath = #"/Users/Anne/Desktop/container.data";
NSData * oneFileOnly = [[NSFileHandle fileHandleForReadingAtPath:containerPath] readDataOfLength:fileOneLength];
// Write result to disk
[oneFileOnly writeToFile:#"/Users/Anne/Desktop/1_one_file.png" atomically:YES];
Tip:
You can also save the 'index' inside the container file.
For example: The first 500 bytes contain the required information.
When you need a specific file: Read the index, get the file position and extract it.
You are archiving a NSMutable array of NSImage. This two classes conform to the NSCoding protocol required by NSKeyedArchiver, so I don't see where would be your problem.
So, here are many ideas to test.
First, are you sure that the data you think you have are valid? In your first code snippet, you write [NSData dataWithContentsOfFile:#"0.png"]. This method expects an absolute file path.
Assuming the problem is not in your code, just in your question, let's continue:
Do you have something different than nil in the variable data after your archiving? Ie, after the assignement to data, can you add this code. If the assertion fail, you will get an exception at runtime:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
NSAssert(nil != data, #"My object data is nil after archiving");
If the problem was not here, what is the return of the line [data writeToFile:path options:NSDataWritingAtomic error:&error];
(Not the variable error, but the return value of the call to the method - writeToFile: options: error:)
What happens if you simplify your code and just do this:
result = [NSKeyedArchiver archiveRootObject:data
toFile:archivePath];
If everything was ok, have you tried to unarchive your file with NSKeyedUnarchiver?
The problem is that [NSData dataWithContentsOfFile:#"0.png"] looks for the file "0.png" in the current directory, but what the application thinks of as the current directory is probably not the place you're expecting. For graphical apps, you should always either use an absolute path or a path relative to some place that you can get the absolute path of (e.g. your app bundle, the application support directory, some user-selected location).
For command-line tools, using the current directory is more common. But I doubt that's the case here.
Another thing I noticed on Mavericks and up is that the folders in the path must be in existence. Meaning you must create the folder structure prior to saving into that folder. If you try to write to a folder on the desktop or elsewhere, even with sandboxing off, it will fail if the folder does not exist. I know this has been answered already, but I found that my issue continued regardless, but once I make sure that the folder structure was in place, I could do my writing to that folder.
On a side note: I'm sure that you could do this from NSFileManager, and I'll be doing that myself once I finalize my app structure, but hope this helps someone else lost in the sauce.

Removing a binary attribute's data(used in NSImageView) in Core Data entity

I have an optional binary attribute: image, containing an image for my entities.
In the interface, I have NSImageView (Image Well), and a "Remove Image" button. When the image removing button is clicked, I do:
- (IBAction)saveAction:(id)sender {
NSError *error = nil;
if (![[self managedObjectContext] save:&error]) {
[[NSApplication sharedApplication] presentError:error];
}
[tableView reloadData];
}
- (IBAction)removeImage:(id)sender {
[image setImage:nil]; // image is a NSImageView outlet bound to the image attribute.
[self saveAction:sender];
}
It clears the image from the NSImageView, but the binary data is still retained in the Core Data entity.
How do I reflect the change in the Core Data entity as well?
Thanks!
Edit:
NSImageView is already bound to model's image attribute, and available as outlet too. So I'm just looking for someone to tell me how to reset the attribute by fetching the model (if that's what I need to do).
Would appreciate any code help. :)
[image setImage:nil];
Is image actually an image view? If so, I must remind you to name your instance variables clearly and accurately.
You need to set the image property of the model object(s), not the view(s). Bind the views through the controllers to the model; then, when you change the model, the views pick up the changes for free.
I was under impression that altering an array from a fetch request won't make a difference to the actual data in storage. But I was wrong. I tried and it worked! Thanks Peter, and everyone elsewhere!
Here's what I replaced my image removal function for currently selected entity having a unique attribute:
- (IBAction)removeImage:(id)sender {
// Fetch the entity in question.
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObjectModel *model = [self managedObjectModel];
NSEntityDescription *entity = [[model entitiesByName] valueForKey:#"myEntity"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"unique_attr == %#", [unique_attr_outlet stringValue]];
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
[fetch setEntity:entity];
[fetch setPredicate:predicate];
// Load it into NSArray object and remove the binary data attribute.
NSArray *contextArray = [context executeFetchRequest:fetch error:nil];
if ([contextArray count] > 0)
[[contextArray objectAtIndex:0] setValue:nil forKey:#"myImage"];
[fetch release];
}