I cannot create NSImage with imageNamed but initWithContentsOfFile - objective-c

With the following code:
NSString *imageString = [[NSBundle mainBundle] pathForResource:#"logo64x64" ofType:#"png"];
NSImage *testImage = [[NSImage imageNamed:#"logo64X64"] retain];
NSImage *testImage2 = [[NSImage alloc] initWithContentsOfFile:imageString];
testImage is nil but testImage2 is a NSImage instance. I don't know what's wrong with the code. I'm sure I can find logo64x64.png and logo64x64#2x.png in the resource directory in the bundle. I've also tried imageNamed:#"logo64X64.png" but still getting nil.
Anyone can help?

Try
[NSImage imageNamed:#"logo64x64"];
rather than
[NSImage imageNamed:#"logo64X64"];
Note that in the code that succeeded, you used a lowercase x, whereas in the code that failed, you used an uppercase X (logo64X64). NSBundle is case-sensitive, even if the underlying HFS+ file system is only case-preserving. (NSImage's +imageNamed: method uses NSBundle to locate resources).

Related

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.

Write in Main Bundle directory. Is it allowed?

I was pretty sure that writing in the main Bundle isn't possible in iOS ... for example an operation like :
NSString *path = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
.....something
[xmlData writeToFile:path atomically:YES];
Why does the first example in Apple's documentation use this exact code?
The example is for OS X, which isn't quite as strict with permissions as iOS.
I'd be surprised if you are able to do that for much longer (if you can at all now) in a Mac App Store application bundle, though.
It could be worth filing a bug regarding the documentation.
That's not a link to the main bundle. That's a path to the resources folder, and a plist within that folder.
Hence the function name pathForResource...
Everything in the main bundle is cryptographically signed when you compile the app. The resources folder isn't though. You can write to and from that freely.
for #jrturton
// we need to get the plist data...
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Symptoms" ofType:#"plist"];
NSMutableArray *dataArray = [[NSMutableArray alloc] initWithContentsOfFile:plistPath];
// add a new entry
NSDictionary *addQuestion = [[NSDictionary alloc] initWithObjectsAndKeys:#"Blank.png",#"Icon",
[NSString stringWithFormat:#"%i",r],#"ID",
[titleTextField text],#"Title",
[questionTextField text],#"Text",
qnType,#"Type",
#"1",#"Custom",
[NSArray arrayWithObjects:#"Yes",#"No",nil],#"Values",
[unitsTextField text],#"Units",
nil];
[dataArray addObject:addQuestion];
[addQuestion release];
// rewrite the plist
[dataArray writeToFile:plistPath atomically:YES];

iOS load an image based on user input

fairly new to Objective-C and iOS development (coming from PHP) and I have a relatively simple question that I can't seem to find an answer to:
I am following along with an example for split View design where a web page is loaded into the Detail View when a user clicks an item in the master view. I got all this working, but would like to substitute web view for an image. So I've amended the app to load a UIImage instead of a WebView. What I'm looking for is the equivalent to this code:
NSString *urlString = [pagesAddress objectAtIndex:indexPath.row];
NSURL *url = [NSURL URLWithString:urlString];
// these 2 is where I get lost with the images.
NSURLRequest = *request = [NSURLRequest requestWithURL:url];
[detailViewController.webView loadRequest:request];
I came up with this:
NSString *imageName = [pagesAddress objectAtIndex:indexPath.row];
UIImage *myImage = [UIImage imageNamed:imageName];
// missing the last 2 calls: one to tell Xcode that it's an image "request" I want and the second to load the actual image (based on it's name that is already in an array) into the ImageView.
Thanks.
PS
I tried this:
NSString *imageName = [pagesAddress objectAtIndex:indexPath .row];
[detailViewController.imageView setImage:[UIImage imageNamed:imageName]];
And it shows just the first image, then crashes when I try to show the last one.
In the end, the solution were those 2 lines when I amended the code:
NSString *imageName = [pagesAddress objectAtIndex:indexPath.row];
[detailViewController.imageView setImage:[UIImage imageNamed:imageName]];
Notice that I had to change the setImage to convert the NSString to a UIImage or Xcode would complain. It turns out it was crashing because in the array where I had the image names, I had put 3 images into one entry (basically I forgot the commas!) so it was out of range.
Tim:
This line you gave me
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
is unnecessary because I already have a view created, it would create another view which I never used. Also, replacing it with CGRect seems overkill if I already have a UIImage placeholder no?
In any case, it works now and I'm very grateful for all the help. iPad development with Objectve-C is a very thorny road and I expect I'll be bugging you guys some more.
Cheers.
Try this:
UIImage *myImage = [[UIImage alloc] initWithData:[NSData dataWithConentsOfURL:[NSURL URLWithString:urlString];
// don't know if you already got the following?
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
[imageView setImage:myImage];
[self.view addSubview:imageView];
The first line is synchronous (= blocking), so in production, you should rather use - [NSURLRequest start] for this (but that's a bit more complicated).
Or use this for your local images:
UIImage *myImage = [UIImage imageNamed:imageName];
// Now, follow the same steps as in the first code-example, just skip the first line.
Try this (on iOS 4.0 and later):
// Execute a block of code on a background thread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^(void)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
UIImage* image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
// When IO is done and image created, set it on the main thread.
dispatch_async(dispatch_get_main_queue(),
^(void)
{
imageView.image = image;
});
[pool release];
});

Thumbnail from NSURL

I am loading files that are downloaded from the web in a UIWebView (HTML files).
Is there a way to create a UIImage from an HTML file located in the Documents folder?
The goal is to auto generate thumbnails without needing to manual create them...
Update
My train of thought would be to do something in the line of:
NSString * s = [[NSBundle mainBundle] pathForResource:#"test" ofType:#"html"];
UIImage *image = [[UIImage alloc] initWithData:
[NSData dataWithContentsOfURL:url]];
Which of course does not work in this case!
Thanks!
S.
I found this solution here
UIGraphicsBeginImageContext(yourWebView.bounds.size);
[yourWebView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *resultImageView = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
resultImageView will be an image that contains the render of yourWebView.
If you want to shrink it down to a thumbnail size from there, I like Trevor's UIImage category for that purpose.

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.