UIImage imageWithContentsOfFile: - include file extensions or no? - ios

Are there any negative implications of excluding the file extension when using [UIImage imageWithContentsOfFile:] or is it best to include the file type in the path?
The Apple Docs state that initWithContentsOfFile: requires the following path as its parameter:
The path to the file. This path should include the filename extension that identifies the type of the image data.
However, the path for [UIImage imageWithContentsOfFile:] just states:
The full or partial path to the file.
I was just wondering if it's still best practice to include the file extension or to not even worry about it. Thanks!

You should include the file extension for all file requests. The only instance I can think of where you would not is using the
NSBundle pathForResource:ofType:
method. That method you specify the file extension outside of the file name. I'm relatively certain the "partial path" refers to relative paths. You can ask for a file in the root of your bundle by just the file name as it's path relative to your bundle is in the root, in other words just the file name.

The best way is to use NSBundle pathForResource:ofType:
If you have those images:
"myImage.png" (iPhone)
"myImage#2x.png" (iPhone Retina)
And you want to load it to an UIImage, you will use this code:
UIImage *myImage;
myImage = [UIImage imageWithContentsOfFile:[NSBundle pathForResource:#"myImage" ofType:#"png"]];
This code will also load the "myImage#2x.png" in iPhone Retina with no need to add the #2x to the code.
Be sure to write the exact file name. "myimage" might not work on device since the original file name has a Uppercase "I" ("myImage.png").

Related

Path Parameter in UIImage imageWithContentsOfFile: for XCAsset

Is there any way to retrieve a UIImage object with imageWithContentsOfFile: if the image is stored in an xcasset Bundle (Xcode 5 Image Catalog)?
It's easy to access it with the imageNamed: method, but imageWithContentsOfFile: has a slightly different behavior, so what do I pass in the path parameter?
I've tried to use a variety of formats, but they all return a nil value:
ImageName#2x.png
ImageName#2x
ImageName
NSBundle File Path to Image
Apple's documentation doesn't specify a format for this. Any ideas?
You can't load an image from xcasset via imageWithContentsOfFile:, because every entry in xcasset is compiled into a single binary file Assets.car. The format of this file is not documented and yet not reverse-engeneered well afaik (see "What you should know if you want to try reverse-engineering this stuff." section for explanations).
This is done for performance and low memory consume reasons, that's why imageNamed: is a good solution.

imageNamed and localization issue

I've localized my images via Xcode, and all the images are in the proper lproj folder.
However, when I use imageNamed I don't get the correct image, but I keep getting the default image even if I change language.
_myImageView.image = [UIImage imageNamed:#"image_name"];
This kind of issue made me waste some time, but then I realized it was a false issue and I hope to save you precious time.
If you switch language and the images are not in the new language, that's because of cached images. The documentation is straightforward:
This method looks in the system caches for an image object with the specified name and returns that object if it exists
Hence, when you switch language, and you ask for the same name to the cache, you'll get the old image! That's it. :)
Instead of:
_myImageView.image = [UIImage imageNamed:#"image_name"];
use:
NSString *imageName = NSLocalizedString(#"image_name", #"image_name");
_myImageView.image = [UIImage imageNamed:imageName];
and localize your image names using the Localizable.strings files like any other resource in your project.

How to pass a relative path (relative to main bundle) to iOS app as "argument passed on launch" in Schemes?

I try to pass a relative path, e.g., subFolder/file, as an argument to my app. The subFolder is placed in Copy Bundle Resources and I made sure that the folder is under myApp.app/Contents/.
I added an argument in my Scheme > Run > Arguments > Arguments Passed On Launch, as
subFolder/file.
However, when running the app, the debugger output says:
/var/mobile/applications/feae1664-6f16-4b96-8b8d-05b4531fe6da/myApp.app/myappbin: cannot open subFolder/file: No such file or directory
myappbin is the executable of the app bundle.
How should I specify a relative path like this?
EDIT
the thing is my app really needs to use that path as a runtime variable, for testing purposes. I do know how to refer to the bundled folders/files in mainBundle in ObjC.
You don't need to pass in the path as an argument. You just need to use the NSBundle methods to return the path.
Have a look at
[[NSBundle mainBundle] pathForResouce: ofType:];
And also the documentation for NSBundle,
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSBundle_Class/Reference/Reference.html

Can't get the CFBundleIdentifier from dynamically loaded NSBundle

I'm trying to load a bundle dynamically, to be used in place of the mainBundle in few cases.
I managed to load the bundle with some resources, like Localizable.strings and so on.
And when I use localizedStringForKey against that bundle, the right localized string is loaded. This is to say that it works.
Nevertheless, I'd like to get even have a bundle identifier. Thus, I added to the root of the bundle folder the info.plist file, containing the CFBundleIdentifier string.
This, doesn't work. When I try to get the identifier via
[myBundle bundleIdentifier]
I get a null value. I tried to name the file both as
Info.plist
and
MyBundle-Info.plist
where MyBundle is the name of the bundle (the content is stored in MyBundle.bundle). But no luck.
I really don't get what's wrong. Do I have to set other keys in the info plist? Or maybe it's a naming problem? Any help will be more than appreciated.
I ended up looking at the Core Foundation (after all, NSBundle is based on CFBundle) source code and figured out where was the problem. So..
The function in charge of gathering the info.plist content is CFBundleGetInfoDictionary. This is called anytime an information theoretically contained in the plist is requested.
Looking at CFBundle.c, the implementation of CFBundleGetInfoDictionary checks whether the _infoDict field of the bundle is initialized and in case it's not, it initializes it:
if (!bundle->_infoDict) bundle->_infoDict = _CFBundleCopyInfoDictionaryInDirectoryWithVersion(CFGetAllocator(bundle), bundle->_url, bundle->_version);
By calling that function on my CFBundle I didn't have any luck, so I guessed something wrong must have been happening in _CFBundleCopyInfoDictionaryInDirectoryWithVersion.
Looking at the source code, I noticed that, depending on the bundle->_version, a different path is used to search the info plist. The version in this case depends on the dir structure used to setup the bundle. To be exact, my version was 0, because, as specified in the function _CFBundleURLLooksLikeBundleVersion (used during bundle initialization), bundle with the Resources dir are this:
// check for existence of "Resources" or "Contents" or "Support Files"
// but check for the most likely one first
// version 0: old-style "Resources" bundles
// version 1: obsolete "Support Files" bundles
// version 2: modern "Contents" bundles
// version 3: none of the above (see below)
// version 4: not a bundle (for main bundle only)
So, to finish the story, the base URL of the Info.plist is initialized in _CFBundleCopyInfoDictionaryInDirectoryWithVersion based on the version 0
infoURLFromBase = _CFBundleInfoURLFromBase0;
that is defined as:
#define _CFBundleInfoURLFromBase0 CFSTR("Resources/Info.plist")
Soooo... I've put my Info.plist in the Resources dir and not outside and now it works.
I guess I'm somehow an idiot for having done all this trip, given that this stuff is probably written somewhere in the doc, but I couldn't find it :(

Loading an image programmatically in iOS

In my App for the iPad I'm trying to load an image file programmatically. The file is NOT part of my project, hence it is not referenced in XCode. There is no entry for the file in XCode's Groups and Files column.
The image has to be loaded at runtime instead, it name being read from a Property List.
I'm trying to load the file like this:
NSString* pathToImageFile = [[NSBundle mainBundle] pathForResource:#"MyImage" ofType:#"png" inDirectory:#"MyDirectory"];
UIImage* retVal = [UIImage imageWithContentsOfFile:pathToImageFile];
In this case, mydirectory lives in the main bundle like this:
MyAmazingApp.app/MyDirectory/MyImage.png
Unfortunately, the Image will not load. I can't add the image file to my project, as its name is to be determined at runtime and cannot be known in advance. The file name is read from a config file (a Property List) at runtime instead.
What am I doing wrong? Your help will be very much appreciated.
You can use NSFileManager to get the contents of a directory, or several directories. You obviously have to know something about the file you want to load so that you can identify it, but you could for example use NSFileManager to help you generate a list of images in your app bundle and in your app's Documents directory.

Resources