I have resources:
Test.png
test1.jpg
tesT2.jpg
tEst3.jpg
I need to show this image in UIImageView. So I wrote:
imgView.image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[myObject.filename stringByDeletingPathExtension] ofType:[myObject.filename pathExtension]]];
myObject is entity in my model. I get filename from xml, but in xml it have format like:
filename="test.png"
filename="test1.jpg"
filename="test3.jpg"
filename="test3.jpg"
So my Image is (null) because in pathForResource I search #"test" ofType "png", but need #"Test" ofType"png".
How I can get correct resources without renaming them?
If you can't rename the source data (which you should do really), then you would need to use the file manager to get a list of all of the files available and then do a case insensitive filter to get the matching name and use that.
You can get all resources from bundle see answers here Count of images in my NSBundle mainBundle bundlePath and manually check names of image that need to load
You should get the list of files in your bundle with the appropriate extension with
NSArray *files = [[NSBundle mainBundle] pathsForResourcesOfType:[myObject.filename pathExtension] inDirectory:nil];
Now you go through the array files and try to match your filename. This is done using NSArray's method - (NSArray *)filteredArrayUsingPredicate:(NSPredicate *)predicate (clean way) or by looping through the array (old school way).
Predicates are described in the Predicate Programming Guide.
For the old school method, simply loop like this:
for (String *file in files)
{
if ([file caseInsensitiveCompare:myObject.filename] == NSOrderedSame)
{
// do whatever you need to do with the file
break; // stop the loop
}
}
How I can get correct resources without renaming them?
Basically iOS contains HFSX file system which is case sensitive. So without renaming the files it is not possible to get the correct resources. Refer this
Also the other alternative is, as #wain said, if you have file store in any directory, then you can just parse from that directory and then do a case insensitive filter to get the matching name and use that for fetching the exact filename from directory and then pass intopathForResourceofNSBundleapi. Refer this for getting the list of file names from directory
Related
If I include a folder in my bundle (a real folder, the blue ones, not the yellow groups), how can I get the path for that folder from my bundle? The method I would usually use is...
[[NSBundle mainBundle] pathForResource:fileName ofType:______];
...but what "type" is a directory? Is there a "type" to use, or is there another method for accessing the paths of directories within the bundle?
Or am I going about this all wrong, and there's some other way for including folders of accessible documents in the bundle?
Directories can have extensions too. If yours doesn't have one, just pass #"" for the type parameter, -[NSBundle pathForResource:ofType:] works for directories too, not only files. At the end, a directory is also a resource :)
As per a suggestion in the comments, it turns out you can use an empty string to refer to folders. So a folder called "myFolder" would be accessible using...
[[NSBundle mainBundle] pathForResource:#"myFolder" ofType:#""];
Hope this helps anyone else who wasn't expecting it to be so easy...
As you're adding a folder by your own, you know the name of the folder, so you can get the path like this:
NSString *myDirectoryName = #"myDirectory";
NSString *absolutePathToMyDirectory = [[NSBundle mainBundle].resourcePath stringByAppendingPathComponent:myDirectoryName];
You'll have to test if this path exist with NSFileManager.
Otherwise you can still use the method you're using, the type can be nil, so you can call it like this
NSString *absolutePathToMyDirectory = [[NSBundle mainBundle] pathForResource:myDirectoryName ofType:nil];
and then testing if the result is nil or not.
if(absolutePathToMyDirectory) {
// do stuff related to this path
}
I have images.xcassets listed ounder copy bundle resources, and I did try to just state the file name by itself: MSB_big_icon , before trying to add the path within images.xcassets.
Can anybody tell me what I'm doing wrong?
NSString *path = [[NSBundle mainBundle]pathForResource:#"/Raymio_android_images/MSB_big_icon.imageset/MSB_big_icon" ofType:#"png"];
NSLog(#"path: %#", path);
MSBIcon *tilecon = [MSBIcon iconWithUIImage:[UIImage imageWithContentsOfFile:path] error:&error];
David Ansermot is right that xcassets is a much better approach and strongly preferred. If you can't use that (running on older versions of iOS for instance), still put everything in one directory and use imageNamed:. This has significant caching benefits over hand-loading the file.
An asset catalog (xcassets) is a (relatively) new, unified way of managing image resources. The images are no longer accessible as separate files on the disk. Instead, imageNamed: consults the asset catalog and fetches the correct asset.
Prior to asset catalogs (and still, for non-images), assets were stored in localized directories. All of your unlocalized assets would be put into a directory called Resources (no matter where those files might appear to be in your source tree, and no matter how those files might be arranged in your Xcode folders). Localized files would be stored in directories like English.lproj or French.lproj. When you make NSBundle calls to load MyImage, it looks at each localized directory in the order the user has configured, and if it cannot find it in any of those directories, it looks in Resources.
Now it is possible to store full directories as "a resource" by marking them as directory references in Xcode. In that case, the whole directory would be copied into Resources or the appropriate localized directory. In order to find files inside such a directory you can use the ...inDirectory: version of the NSBundle methods.
So most of the time, you want to just use imageNamed:, which is going to fetch things out of the asset catalog if available, and then search localized directories, and then look in Resources. If you need to find a non-image, or if for some reason you want the real path to the file, you can compute it like this:
NSString *path = [[NSBundle mainBundle] pathForResource:#"MSB_big_icon" ofType:#"png"];
And if that resource were in a directory tree (because it was a directory reference in Xcode), you can access it like this:
NSString *path = [[NSBundle mainBundle] pathForResource:#"MSB_big_icon"
ofType:#"png"
inDirectory:#"Raymio_android_images/MSB_big_icon.imageset"];
Here's a code exemple from one of my apps :
NSString *appKey = #"Applications__GENERIC";
NSString *path = [[NSBundle mainBundle] pathForResource:appKey ofType:#"plist"];
appData = [NSDictionary dictionaryWithContentsOfFile:path];
The "Applications__GENERIC.plist" is stored like this :
Other solutions :
Use the images.xcassets.
Then in your code to load an image, use the code :
UIImage *image = [UIImage imageNamed:#"MyImageWithoutExtension"];
Don't put any path or extension, only the image's name
Try using this:
NSString *path = [[NSBundle mainBundle] pathForResource:#"MSB_big_icon" ofType:#"png" inDirectory:#"Raymio_android_images/MSB_big_icon.imageset"];
What you can also do to debug is to print out
[[NSBundle mainBundle] bundleURL]
Then navigate to that folder and see if the folder structure corresponds to the path you use.
I just struggled with this today, hope it works out for you too.
I have created a plist on XCode that will have a few values that I can't insert manually. So I want to add this values programmatically at development time. But it seems that I can only read the plist I can not save a plist that is on the App bundle, which makes sense at runtime.. When I will distribute my app I want everyone to have this plist file that's why I am not saving on documents or cache. How can I achieve what I want?
From http://www.karelia.com/cocoa_legacy/Foundation_Categories/NSFileManager__Get_.m (pasted below) you can build a path within the user's personal library with the -(NSString *) pathFromUserLibraryPath:(NSString *)inSubPath method found there.
For example, NSString *editedPlist = [self pathFromUserLibraryPath:#"my.plist"]; gets you the name of the modified plist within the user's library (even if that plist doesn't exist yet).
How you read/write it is according to what kind of plist you have, but you could read it into a dictionary with:
NSMutableDictionary *thePlist= [[NSMutableDictionary alloc] initWithContentsOfFile:editedPlist ];
If you are unable to read, easily detected by, for example [thePlist count] == 0, then you would instead call the same initWithContentsOfFile: initializer with a path to the template within your bundle, but you would then write the plist out to the editedPlist path so it appears in the user directory.
Here is the utility method I referenced above:
/*
NSFileManager: Get the path within the user's Library directory
Original Source: <http://cocoa.karelia.com/Foundation_Categories/NSFileManager__Get_.m>
(See copyright notice at <http://cocoa.karelia.com>)
*/
/*" Return the path in the user library path of the given sub-path. In other words, if given inSubPath is "foo", the path returned will be /Users/myUser/Library/foo
"*/
- (NSString *) pathFromUserLibraryPath:(NSString *)inSubPath
{
NSArray *domains = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask,YES);
NSString *baseDir= [domains objectAtIndex:0];
NSString *result = [baseDir stringByAppendingPathComponent:inSubPath];
return result;
}
What I would suggest is writing code that checks for the plist in the documents directory at start. If it's there, read it into memory.
If you don't find the file in the documents directory, read it from the app bundle instead. Then drop into the code that uses it from memory and writes the changed version to the documents directory.
Remember that all the objects you read from a plist file are read as immutable, even if you wrote mutable objects into the file. You have to write code that makes mutable copies of anything that you want to change. (And have to implement a mutable deep copy if you have complex structures like arrays of dictionaries that in turn contain arrays of strings.)
I have a method:
#define ROOT_ASSETS_PATH #"/assets/"
...
-(NSMutableDictionary*) getContentsOfPlist{
NSString *pageContentPlistPath = [[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: ROOT_ASSETS_PATH] stringByAppendingPathComponent:#"content.plist"];
NSMutableDictionary *mDictionary = [NSMutableDictionary dictionaryWithContentsOfFile:pageContentPlistPath];
return mDicitonary;
}
On the simulator. this returns a dictionary. On the iPad, it returns nil. Made sure the extension case matches that in the call as well.
Any ideas?
The issue was case after all. Case of the filename didn't match the case I put in the parameter. OSX doesn't care, but the iPad does.
Try removing the slashes in your #define. stringByAppendingPathComponent will add the slashes for you. Not sure why this would work in the Simulator.
Also it is good idea to use:
[NSPropertyListSerialization propertyListWithData:options:format:error:]
Instead of direct NSDictionary initialization. If something will fail, it should return a nice error for you.
+ (id)dictionaryWithContentsOfFile:(NSString *)path
Parameters
path
A full or relative pathname. The file identified by path must contain
a string representation of a property list whose root object is a
dictionary.
Return Value
A new dictionary that contains the dictionary at path, or nil if there
is a file error or if the contents of the file are an invalid
representation of a dictionary.
So, you have to set a full path to your file (or an appropriate relative path)
Note also that the app bundle is read-write on Simulator but read-only on device.
My project has a folder (not group) named "data" which contains many subfolders, each of which contains a set of files.
My question is, how do I grab a URL (or path) reference to the "data" folder? How about to its subfolders? I'm sure it's a fairly simple task, so forgive my ignorance, but I've never used folder refs in a project so I'm not familiar with the code. I did look over the NSFileManager ref but I'm fuzzy on how to make use of it.
Thanks in advance.
NSBundle can give you your absolute path in the system.
NSString (or NSURL) has methods for working with paths.
NSFileManager allows you to move, copy, delete (…) files.
This is how you get path to your Data directory:
NSString *dataDir = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"Data"];
// "/var/private/Application/.../YourApp.app/Data"
Now you just append multiple directory names to dataDir using the same method above and you should get any path you want.
In case you don't know the exact path and you want to scan the directory, you will have to use
:
NSArray *dataDirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dataDir error:nil]
// "file1.data", "file2.data", ...
Then it's all about appending path components.