5

I am downloading two images from the web Apple.png and Apple@2x.png. I want to use [UIImage imageNamed:@"Apple.png"] so it can use the build in features to detect whether it should display either Apple.png or Apple@2x.png.

Now where do I store these images? I read the following in the documentation:

The name of the file. If this is the first time the image is being loaded, the method looks for an image with the specified name in the application’s main bundle.

Ah so the Application's Main Bundle is the way to go. This is what my code looks like:

NSString      *directory   = [[NSBundle mainBundle] bundlePath]; 
NSString      *path        = [directory stringByAppendingPathComponent:imageName];
NSFileManager *fileManager = [NSFileManager defaultManager];

[fileManager createFileAtPath:path contents:dataFetcher.receivedData attributes:nil];

I checked if the file was created in the folder of the path and that was correct. I also dragged an Example.png in my project file just to see if it got stored in the same folder and that also was correct.

However, [UIImage imageNamed:@"Apple.png"], still fails to fetch the images.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Mark
  • 16,906
  • 20
  • 84
  • 117
  • You can't create files in your main bundle on the iPhone. The simulator may let you do it, but the device won't/shouldn't. If you're downloading, you'll have to put it somewhere else. – Ben Zotto Jan 21 '11 at 01:35
  • Your category breaks on for instance iPad (1st gen) if only @2x is implemented (ie 1x would be needed as well). Probably breaks on iPhone 3GS too under same circumstance. – Jonny Nov 05 '12 at 04:28
  • I moved your solution to a community wiki answer. – Cœur Mar 12 '18 at 01:52

6 Answers6

2

You cannot use UIImage'a +imageNamed: method with pictures downloaded by the app. That method looks for images in the app bundle indeed, but your app is not allowed to change its own bundle at run time.

Johan Kool
  • 15,637
  • 8
  • 64
  • 81
  • Weird, I don't get an error and also the FileManager says it's ok. But if UIImage imageNamed does not work for the mainBundle. Is there an alternative to get the proper image? – Mark Jan 21 '11 at 01:40
  • Error or not, it is definitely not a good idea to write to your app's bundle. See http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/RuntimeEnvironment/RuntimeEnvironment.html – Johan Kool Jan 21 '11 at 01:45
1

Download the images (asynchronously of course) to the store local to your app (ie, your sandbox), and reference them locally. You can then use NSDocumentDirectory or NSCachesDirectory to always obtain a reference to their location.

Alan Zeino
  • 4,406
  • 2
  • 23
  • 30
  • When I store the images in NSDocumentDirectory it will also not work I will try NSCachesDirectory now. – Mark Jan 21 '11 at 01:38
  • See Johan's answer. That said, while `+imageNamed:` is a nice solution, it isn't too difficult to use `[[UIScreen mainScreen] scale] == 2.0`. – Alan Zeino Jan 21 '11 at 01:40
  • Thanks I added it into my own category. – Mark Jan 21 '11 at 02:09
0

I think the best solution is to make in Library folder your own bundle e.g. images.bundle. There you can strore your images for both resolutions. To access that images you can use pathForResource:ofType: method of NSBundle class. It returns appropriate images. Than init image with returned path.

NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
NSURL *libraryURL = [fileManager URLForDirectory:NSLibraryDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:&error];
if (libraryURL == nil) {
    ALog(@"Could not access Library directory\n%@", [error localizedDescription]);
} else {
    NSURL *folderURL = [libraryURL URLByAppendingPathComponent:@"images.bundle"];
    DLog(@"Future bundle url = %@",folderURL);
    if (![fileManager createDirectoryAtURL:folderURL withIntermediateDirectories:YES attributes:nil error:&error]) {
        ALog(@"Could not create directory %@\n%@", [folderURL path], [error localizedDescription]);
    } else{
        NSBundle *bundle = [NSBundle bundleWithURL:folderURL];
        // put image and image@2x to the bundle;
        [bundle pathForResource:yourImageName ofType:@"png"];
    }
}
DanSkeel
  • 3,853
  • 35
  • 54
0

Are the files being created with appropriate permissions for you to access them? Did you make sure to close the file after saving it?

blueberryfields
  • 45,910
  • 28
  • 89
  • 168
0

You cannot modify your bundle at runtime. I also think that imageNamed uses something like a resources map initialized within the app containing the bundle resources references.

Try this instead: http://atastypixel.com/blog/uiimage-resolution-independence-and-the-iphone-4s-retina-display/

Ciao!

lomanf
  • 2,039
  • 1
  • 16
  • 14
0

Solution by OP.

Category for UIImage:

@implementation UIImage (ImageNamedExtension)

+ (UIImage *)imageNamed:(NSString *)name fromDirectory:(NSString *)directory {

    if ([UIScreen mainScreen].scale >= 3.0) {
        NSString *path3x = [directory stringByAppendingPathComponent:[[name stringByDeletingPathExtension] stringByAppendingFormat:@"@3x.%@", name.pathExtension]];
        UIImage *image = [UIImage imageWithContentsOfFile:path3x];
        if (image) {
            return image;
        }
    }
    if ([UIScreen mainScreen].scale >= 2.0) {
        NSString *path2x = [directory stringByAppendingPathComponent:[[name stringByDeletingPathExtension] stringByAppendingFormat:@"@2x.%@", name.pathExtension]];
        UIImage *image = [UIImage imageWithContentsOfFile:path2x];
        if (image) {
            return image;
        }
    }
    NSString *path = [directory stringByAppendingPathComponent:name];
    return [UIImage imageWithContentsOfFile:path];
}

@end
Cœur
  • 37,241
  • 25
  • 195
  • 267