I'm trying to populate an NSArray with a collection of images in Resources. However, for maximum flexibility, I'm trying to avoid hard-coding the filenames or even how many files there are.
Normally, I'd do something like this example from the sample code at apple:
kNumImages = 5; //or whatever
NSMutableArray *images;
for (i = 1; i <= kNumImages; i++)
{
NSString *imageName = [NSString stringWithFormat:#"image%d.jpg", i];
[images addObject:[UIImage imageNamed:imageName];
}
However, I'm trying to avoid kNumImages entirely. Is there a way to run a regex or something on resources?
Here's a snippet that does just that from my iPhone app
// Load item icons
paths = [[NSBundle mainBundle] pathsForResourcesOfType:#"png" inDirectory:nil];
for (NSString *filename in paths) {
filename = [[filename componentsSeparatedByString:#"/"] lastObject];
if ([filename hasPrefix:#"ItemIcon"]) {
[UIImage imageNamed:filename];
}
}
It loops through all resources that have a png extension, and it the filename begins with "ItemIcon" then it loads into UIImage's built in cache.
If you have them in a specific directory, you will need to specify the indirectory: argument.
Related
I have an application which donwloads several images and stores them on the phone. In total it will probably required around 20 images tops. I need to be able to retrieve any of these images at will depending on what screen the user is on. These images will be stored indefinitely, so I don't want to use temp directory.
At present I have a class named Images with these methods
- (void) cacheImage: (NSString *) ImageURLString : (NSString *)imageName
{
NSURL *ImageURL = [NSURL URLWithString: ImageURLString];
// Generate a unique path to a resource representing the image you want
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex: 0];
NSString *docFile = [docDir stringByAppendingPathComponent: imageName];
// Check for file existence
if(![[NSFileManager defaultManager] fileExistsAtPath: docFile])
{
// The file doesn't exist, we should get a copy of it
// Fetch image
NSData *data = [[NSData alloc] initWithContentsOfURL: ImageURL];
UIImage *image = [[UIImage alloc] initWithData: data];
// Is it PNG or JPG/JPEG?
// Running the image representation function writes the data from the image to a file
if([ImageURLString rangeOfString: #".png" options: NSCaseInsensitiveSearch].location != NSNotFound)
{
[UIImagePNGRepresentation(image) writeToFile: docFile atomically: YES];
}
else if([ImageURLString rangeOfString: #".jpg" options: NSCaseInsensitiveSearch].location != NSNotFound ||
[ImageURLString rangeOfString: #".jpeg" options: NSCaseInsensitiveSearch].location != NSNotFound)
{
[UIImageJPEGRepresentation(image, 100) writeToFile: docFile atomically: YES];
}
}
}
- (UIImage *) getCachedImage : (NSString *)imageName
{
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* cachedPath = [documentsDirectory stringByAppendingPathComponent:imageName];
UIImage *image;
// Check for a cached version
if([[NSFileManager defaultManager] fileExistsAtPath: cachedPath])
{
image = [UIImage imageWithContentsOfFile: cachedPath]; // this is the cached image
}
else
{
NSLog(#"Error getting image %#", imageName);
}
return image;
}
-(void)getImages
{
//example
NSString *image1URL = #"http://test/image1.png";
NSString *image2URL = #"http://test/image2.png";
NSString *image3URL = #"http://test/image3.png";
[self cacheImage:sLogo: #"Image1"];
[self cacheImage:sBlankNav: #"Image2"];
[self cacheImage:buttonLarge :#"Image3"];
}
-(void) storeImages
{
image1 = [self getCachedImage:#"Image1"];
image2 = [self getCachedImage:#"Image2"];
image3 = [self getCachedImage:#"Image3"];
}
So I use the code like this
Images *cache = [[Images alloc]init];
[cache storeImages];
The get images method is called once when the app first starts to get the images, it isn't called again after that, unless the images on the server are updated and I need to retrieve the updated ones.
The code works, but the problem is when I navigate to a screen that uses it, there is a very slight delay before the screen loads as it is loading the images.
My application is a tabbed application, so it begins on tab 1, I click tab 2 which implements the code, there will be a slight pause the first time it loads. It doesn't last very long, but it is noticeable and is very annoying. After that it is fine, as it is already loaded. However with navigation controller, every time you move from the first VC to the second VC, the method will be called again, so each time you navigate the delay will be there.
The images are not very big, biggest one is 68kb, others are much smaller than that. At present I am just testing with 5 images. Is there a more efficient way of storing and retrieving images, or am I doing something wrong with my code? I need to be able to retrieve these images without any noticeable delay in order for my application to remain fluid and not jerky or clunky.
Thanks in advance!!
You have two options to do the image loading work on a background thread - use Grand Central Dispatch or NSInvocationOperation. GCD might be considered the cleaner of the two:
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(q, ^{
//load images here
dispatch_async(main, ^{
// show on main thread here
});
});
you have delay because you're downloading data synchronously
// NSData *data = [[NSData alloc] initWithContentsOfURL: ImageURL];
Try some smart library like SDWebImage:
it lets you download image asynchronously while you still can display a local image (a proxy image). By the way, you still get cache image for free. So even if u are on local, you can still catch previously downloaded images
https://github.com/rs/SDWebImage
A must have
I have the following code below to populate an array with images:
NSString *fileName;
myArray = [[NSMutableArray alloc] init];
for(int i = 1; i < 285; i++) {
fileName = [NSString stringWithFormat:#"Animation HD1.2 png sequence/HD1.2_%d.png", i];
[myArray addObject:[UIImage imageNamed:fileName]];
NSLog(#"Loaded image: %d", i);
}
In my resources folder i have #2x versions of each of these images. Is there a way (programmatically) that I can ignore the #2x images on retina devices and populate the array with the non-#2x images?
EDIT 1:
I've edited my code to use NSData:
myArray = [[NSMutableArray alloc] init];
for(int i = 1; i < 285; i++) {
fileName = [NSString stringWithFormat:#"Animation HD1.2 png sequence/HD1.2_%d.png", i];
NSData *fileData = [NSData dataWithContentsOfFile:fileName];
UIImage *regularImage = [UIImage imageWithData:fileData];
[myArray addObject:regularImage];
}
falling.animationImages = MYArray;
This is crashing my app with the error: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'. Am i using the NSData object wrong?
I believe the question amounts to "How do I bypass the automatic #2x image loading?"
You need to take a path it can't follow. You could pass the contents of each file using NSData dataWithContentsOfFile:options:error: to UIImage imageWithData: This way, imageWithData won't know where the data came from, let alone that there's a 2x version.
I think the NSData technique should work, but you need to get the path from your bundle, you can't just give the filename as a string like that.
Try:
filename = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"Animation HD1.2 png sequence/HD1.2_%d.png", i] ofType:nil];
Try this...
//This string will have #"#2x.png"
NSString *verificationString = [myString substringFromIndex:[myString length] - 7];
if(![verificationString isEqualToString:#"#2x.png"])
{
//NOT EQUAL...
}
I would like to make my code a little better.
This is my array:
//All images - Add images to the queue
imagesQueue = [[NSMutableArray alloc] init];
[imagesQueue addObject:[UIImage imageNamed:#"climbing_001.jpg"]];
[imagesQueue addObject:[UIImage imageNamed:#"climbing_002.jpg"]];
[imagesQueue addObject:[UIImage imageNamed:#"climbing_003.jpg"]];
[imagesQueue addObject:[UIImage imageNamed:#"climbing_004.jpg"]];
All my images are inside an images folder in my resources.
Is there a way to automatically create an array from all the images in that folder?
NSBundle has a number of methods to help here. An example:
NSArray* imagePaths = [[NSBundle mainBundle] pathsForResourcesOfType:#"jpg" inDirectory:imagesFolder];
imagesQueue = [[NSMutableArray alloc] initWithCapacity:imagePaths.count];
for (NSString* path in imagePaths)
{
[imagesQueue addObject:[UIImage imageWithContentsOfFile:path]];
}
Use NSFileManager methods to discover the content of a directory.
Use NSBundle's resourcePath method to get this given path to the resources folder uour images are in, or directly the paths to those files using methods such as pathsForResourcesOfType:inDirectory:.
But I am not sure that base your code on the contents of your resources directory is the right approach. The best solution is probably to still setting the names of images in your code (instead of iterating into your folder contents), and set the images using a loop:
for(int i=0;i<4;++i) {
NSString* imageName = [NSString stringWithFormat:#"climbing_%03d.jpg",i+1];
[imagesQueue addObject:[UIImage imageNamed:imageName]];
}
Here is what I'm doing, when I create an image with the path in the bundle:
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"image" ofType:#"jpg"]];
What I want to do is trying to find the path for my image but without using the extension, without using 'ofType' (because the name of my image and her extension is store in my database) something like that:
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"image.jpg"]];
But I don't know how to do it.
Best regards,
Why don't you split the string that you get from the DB?
NSString* fullFileName = #"image.jpg";
NSString* fileName = [[fullFileName lastPathComponent] stringByDeletingPathExtension];
NSString* extension = [fullFileName pathExtension];
Now you can simply use:
[[NSBundle mainBundle] pathForResource:fileName ofType:extension]];
You can easily use the NSBundle method without passing the extension, just pass nil for extension.
[[NSBundle mainBundle] pathForResource:#"image.jpg" ofType:nil];
- (NSData *)applicationDataFromFile:(NSString *)fileName {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:fileName];
NSData *myData = [[[NSData alloc] initWithContentsOfFile:appFile] autorelease];
return myData;
}
taken from http://developer.apple.com/iphone/library/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/FilesandNetworking/FilesandNetworking.html#//apple_ref/doc/uid/TP40007072-CH21-SW21
Could be easily adapted if you wanted it to return a UIImage instead of an NSData.
Also, you don't say if you are saving the images to the documents directory, or adding them to your app bundle before compiling. because if it's the latter, you can use [UIImage imageNamed:(NSString *)filename] to get the image. It expects an extension as part of the file-name.
The easiest way is to store the name and file type in your database separately, and retrieve them using the first method.I'm not sure that you can be successful in implementing the latter one.
I found that with an extension of ".jpg" it was necessary to use ofType for the extension for the app to work on an iPod Touch, whereas with an extension of ".png" I could just put "image.png" in pathForResource and say ofType:nil. But all versions worked on the simulator.
The app bundle contains the image file, and I am using:
[[NSBundle mainBundle] pathForResource:#"Auto" ofType:#"jpg"]
to get a path.
hi Guys I have worked on Application and I am trying to Count the No of images in my resource folder and display no.of images in the Simulator
Could any body tll me how to do ?
give me code
If you want to do this, you'll need to use NSFileManager to look through the contents of [[NSBundle mainBundle] bundlePath] (the path of your app bundle) to find all the files with the right extension.
A better question: why do you want to do this?
Edit: alright, if you insist, you'll have to do something like this:
NSEnumerator *iter = [[NSFileManager defaultManager] directoryEnumeratorAtPath:[[NSBundle mainBundle] bundlePath]];
int count = 0; // number of images
for (NSString *path in iter) { // iterate through all files in the bundle
if ([[path pathExtension] isEqualToString:#"png"]) { // test if the extension is "png"
count++; // increment the number of images
UIImage *img = [UIImage imageWithContentsOfFile:path];
// do other things with the image
}
}