How can I load an image from Assets.car (compiled version of xcassets) within an NSBundle? (without using CocoaPods) - ios

We're getting these kind of error messages:
Could not load the "iconStatus" image referenced from a nib in the bundle with identifier "com.company.OurResourceBundle".
Basically, we put a bunch of images in the xcassets folder ( which works for non-bundle loaded cases ). The xcassets and nib files are packed into a resource bundle.
When the app launches,
uiimageview in the nib file cannot load any images with the above error message.
[UIImage imageNamed:#"OurResourceBundle.bundle/iconStatus"] returns nil
The question is related to "How can I load an image from Assets.car (compiled version of xcassets) within an NSBundle?", but we don't use CocoaPods.

In iOS 7 it is not possible to load images from any .car file aside from the one that Xcode compiles into the main bundle. This was confirmed by an Apple Developer at https://devforums.apple.com/message/968859#968859:
Unfortunately it is not possible to load images from any car file aside from the one that Xcode compiles into your main bundle, as +imageNamed: does not accept a bundle parameter, which is what is needed to do so (and even then it would only be able to open a single asset catalog in a single bundle).
In iOS 8 there is a new method that appears to do what you want:
+ (UIImage *)imageNamed:(NSString *)name inBundle:(NSBundle *)bundle
compatibleWithTraitCollection:(UITraitCollection *)traitCollection

you have to define your bundle
imageView.image = UIImage(named: "iconName", in: Bundle.main, compatibleWith: nil)

I suspect if your image assets folder gets compressed into a .car file then NSBundle will know how to search for assets inside of it. Try creating a new bundle from your resources bundle. Then ask that bundle for the path of your image. Finally create an image from that path. This is sort of the approach we take when bundling image assets and localized strings with our static libraries. Our version is a little more involved as it takes care of things like #2x and ~ipad images automatically but this is the gist of it.
NSBundle *myBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:#"your bundle" ofType:#"bundle"]];
NSString *filePath = [myBundle pathForResource:#"icon" ofType:#"png"];
UIImage *image = [UIImage imageWithContentsOfFile:filePath];

Related

UIImage load wrong image in main bundle

I had a main project contain more than 10 bundles. I had two image named map_bubble_right_normal, one in main bundle, the other one in sub bundle(SCommon.bundle). I write code as follow:
UIImage *image = [UIImage imageNamed:#"map_bubble_right_normal"];
The code was writen in didFinishLaunchingWithOptions of AppDelegate.
I want to load the image which I saved at main bundle. However, the result of the code that load image from sub bundle(SCommon.bundle).
I guess that image saved at main bundle maybe a error image(maybe copy problem). Therefore, I show the ipa content to see the "map_bubble_right_normal" file saved at root directory. The image is right!!!!!
I don't know why imageNamed: would load the image from sub bundle(SCommon.bundle). I tried to delete app and restart iPhone, the result is same. And, I tried to delete Derived Data of XCode, and clean the project. The image still load from sub bundles(SCommon.bundle).
Addition, I test the problem at iOS 8 and iOS 9 device.
Only a method which change the bundleId(main bundle) can solve the problem temporary so far.
I know the cache feature of UIImage, but I can't understand why did the strange scene will happen.
I sincerely request you a great god for help. Thank you~
Thank you for everyone's answer. I sorry about I had mislead all of us to bundle.
I found the cause of the problem. The Error Image isn't came from sub bundle, which came from Assets.car. The cause of the problem should be blame to Image.assert in physical directory actually. CocoaPods will copy all match "*.assets" to Assets.car.
I had made same question at Apple Developer forum, and found the key tip of the problem.
The issue of Cocoapods which relative to the problem Pods copy resource script overrides default xcasset bahaviour.
Pod_resources.sh script would copy every *.xcassets in Pod_Root if your Pod Repo had add any folder which name match *.xcassets.
Key Code of Pod_resources.sh:
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
...
# Find all other xcassets (this unfortunately includes those of path pods and other targets).
OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
while read line; do
if [[ $line != "`realpath $PODS_ROOT`*" ]]; then
XCASSET_FILES+=("$line")
fi
...
fi
Condition XCASSET_FILES will be fulfill if your Pod had add any xcassets file.
To load image from main bundle try this:
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"map_bubble_right_normal" ofType:#"png"]];
If you want to load from mainBundle try this code:
NSBundle* myBundle = [NSBundle mainBundle];
NSString* myImage = [myBundle pathForResource:#"Seagull" ofType:#"jpg"];
If you want to load from sub bundle:
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"YourBundleName" ofType:#"bundle"];
NSBundle *subBundle = [NSBundle bundleWithPath:bundlePath];
NSString* myImage = [subBundle pathForResource:#"Seagull" ofType:#"jpg"];
Refer here
I've occured some same situation. Instead of two bundle you mentioned above, I failed to show the right image with the exact right name. And finally, I found the key point is that the image has a suffix of jpg instead of png. I changed to an image of png and all turned well. Maybe it's a possible situation you occured. Have a try and good luck. :)
PS: Here's a related link as a reference.

How to transfer resource files with Static Library (How to wrap resources in bundle)?

I'm creating a Static Library for iOS applications. I almost completed it, but there is an issue with the resources.
My Static Library uses a lot of images and sound files. How can I transfer it with my Static Library ?
I know we can wrap it on a bundle and give it along the .a file. But I don't know how to wrap Images and sound files in a Bundle file.
What I did:
I searched a lot, but couldn't find any useful links.
I got Conceptual CFBundles reference, but didn't find any solution for my problem.
I checked the file templates available for XCode, but didn't saw any bundle type other than Settings Bundle.
There are several good reasons to build an application with multiple bundles and several different ways to do it. To my experience the best way is open Xcode and create a new bundle project:
Select: File -> New Project… -> Group Mac OSX (!) -> Framework & Library -> Bundle. Add your resource files to the project.
Build the bundle as you build other iPhone apps.
You may add this project to your static library project and rebuild it all the time the library is changed. You must know that the bundle itself will not be linked to your library file.
In your app projects add the .bundle file to your project as an ordinary resource file (Add -> Existing Files… -> find and select the above built .bundle file. Do not copy it).
Example :
// Static library code:
#define MYBUNDLE_NAME #"MyResources.bundle"
#define MYBUNDLE_IDENTIFIER #"eu.oaktree-ce.MyResources"
#define MYBUNDLE_PATH [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: MYBUNDLE_NAME]
#define MYBUNDLE [NSBundle bundleWithPath: MYBUNDLE_PATH]
// Get an image file from your "static library" bundle:
- (NSString *) getMyBundlePathFor: (NSString *) filename
{
NSBundle *libBundle = MYBUNDLE;
if( libBundle && filename ){
return [[libBundle resourcePath] stringByAppendingPathComponent: filename];
}
return nil;
}
// .....
// Get an image file named info.png from the custom bundle
UIImage *imageFromMyBundle = [UIImage imageWithContentsOfFile: [self getMyBundlePathFor: #"info.png"] ];
For more help you can check these good articles
iOS Library With Resources
Resource Bundles
Hope it helps you.

iOS Static Library with Image resources

I have a static library where I need some graphics resources as Images.
In my class I would to add an image in this way:
[UIImage imageNamed:#"myImage.png"]
but it not work...
how can I do it?
You can't provide anything other than code with a static library. This is because .a files are a simple archive of object files, with the added ability under Mac/iOS of supporting multiple CPU architectures.
The only option you have is to create a Framework, which can provide code as well as other resources.
Once you have that bundle, you can access to the resources you have there like this:
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"YourBundle" ofType:#"bundle"];
NSString *imagePath = [[NSBundle bundleWithPath:bundlePath] pathForResource:#"yourImage" ofType:#"png"];
UIImage *image= [[UIImage alloc] initWithContentsOfFile:imagePath];
In Xcode, if you link a static library, the code from the library is included directly into your executable. No files are copied to the application bundle, so there's no way to copy the image.
If you have the image file with your static library, you can simply copy it to your application bundle by adding a copy files build phase to your target in Xcode.
I found the solution ..
I explored the bundles directory (compiled) and I've noticed that my images (.png) they had all the iestensione .tiff
To avoid this I set the key HIDPI_COMBINE_IMAGES in my bundle buid setting as NO
..so I can recover my pictures as suggested by e1985
Make a Framework target, not static library.
Make sure "Copy Bundle Resources" exists in Build Phases for your Framework. Add your resource files there.
Then to find the file in the Framework bundle from an outside project I just do this:
NSArray<NSBundle *> * bundles = [NSBundle allFrameworks];
NSString * filePath;
for (int i = 0; i < bundles.count; i++) {
filePath = [[bundles objectAtIndex:i] pathForResource:#"picture" ofType:#"png"];
if (filePath != nil) break; // found
}

IOS Static Framework with resources inside

I'm trying to create a framework that keeps some common code I use around in my projects.
I found online tutorials and managed to create a framework but I still have one problem related to resources (xibs, images, etc).
Let's say I have a MainViewController.xib which has a UIImageView using a test.png. All these are in my framework package.
When I use the framework in another project, I add it to the "Copy Bundle Resources" build phase. The problem is that the xib is only accessible using a path like dummy.framework/Resources/MainViewController.xib and the UIImageView inside can't load test.png.
It seems that the UIImageView will try to load the png from the root of the bundle, and not from the relative folder where the xib is also stored.
Has anybody managed to create a framework with code and resources and use it in another project?
I know this is an old thread, but to keep new people from making the same mistakes here is a note:
As of Xcode 6 and iOS8, Apple has a fully supported first party Dynamic Framework solution built in. For more information look here: https://developer.apple.com/library/ios/documentation/DeveloperTools/Conceptual/WhatsNewXcode/Articles/xcode_6_0.html#//apple_ref/doc/uid/TP40014509-SW14
But the short of it is, create a new project and choose the Cocoa Touch Framework template.
I created a framework target and to access images inside its asset catalog I did the following:
UIImage *img = [UIImage imageNamed:#"IMAGE_NAME_HERE" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:[UITraitCollection traitCollectionWithDisplayScale:[UIScreen mainScreen].scale]];
I don't manually manage frameworks anymore but use and recommend CocoaPods instead.
Original answer:
Use the fake framework #wattson12 mentioned. It will compile and save the resources as well.
Inspired by this script, add this to your target to copy the resources to your app:
SOURCE_PATH="${TARGET_BUILD_DIR}/MYFramework.framework/Resources/"
TARGET_PATH="${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MYFrameworkResources.bundle"
mkdir -p $TARGET_PATH
cp -R $SOURCE_PATH $TARGET_PATH
You could also just drag the framework to your Copy Resources step, but then you'll be adding unnecessary headers and compiled code as well.
Edit
To use these resources from IB, for instance a png file, replace:
MyImage
by:
MYFrameworkResources.bundle/MyImage.png
It will preview a broken image icon but will work when running.
Load a Nib from code:
[NSBundle loadNibNamed:#"MYFrameworkResources.bundle/MyNib" ...
Finally you can add these methods in a NSBundle category to ease access to Nib resources that my be in your main bundle or in MYFrameworkResources.bundle:
#implementation NSBundle (MyCategory)
+ (NSString *)pathForResource:(NSString *)name
ofType:(NSString *)extension
{
// First try with the main bundle
NSBundle * mainBundle = [NSBundle mainBundle];
NSString * path = [mainBundle pathForResource:name
ofType:extension];
if (path)
{
return path;
}
// Otherwise try with other bundles
NSBundle * bundle;
for (NSString * bundlePath in [mainBundle pathsForResourcesOfType:#"bundle"
inDirectory:nil])
{
bundle = [NSBundle bundleWithPath:bundlePath];
path = [bundle pathForResource:name
ofType:extension];
if (path)
{
return path;
}
}
NSLog(#"No path found for: %# (.%#)", name, extension);
return nil;
}
+ (NSArray *)loadNibNamed:(NSString *)name
owner:(id)owner
options:(NSDictionary *)options
{
// First try with the main bundle
NSBundle * mainBundle = [NSBundle mainBundle];
if ([mainBundle pathForResource:name
ofType:#"nib"])
{
NSLog(#"Loaded Nib named: '%#' from mainBundle", name);
return [mainBundle loadNibNamed:name
owner:owner
options:options];
}
// Otherwise try with other bundles
NSBundle * bundle;
for (NSString * bundlePath in [mainBundle pathsForResourcesOfType:#"bundle"
inDirectory:nil])
{
bundle = [NSBundle bundleWithPath:bundlePath];
if ([bundle pathForResource:name
ofType:#"nib"])
{
NSLog(#"Loaded Nib named: '%#' from bundle: '%#' ", name, bundle.bundleIdentifier);
return [bundle loadNibNamed:name
owner:owner
options:options];
}
}
NSLog(#"Couldn't load Nib named: %#", name);
return nil;
}
#end
It will first look into your application bundle and then in MYFrameworkResources.bundle, etc.
In order to load an image from a dynamic framework in Swift 3:
UIImage(named: "name", in: Bundle(for: type(of: self)), compatibleWith: nil)
Basic frameworks do not include most kinds of resources, to include resources use this "fake" framework library
You end up with a .embeddedframework folder which contains the actual framework, but also includes any resources which you add to the Copy Bundle Resources build phase. Ive used it to include xibs, core data models, images, and plists
Short answer: you can't.
When you compile the app, the bundle is generated with the resources of the "main" project, ignoring the resources in linked frameworks. This includes images, xib's, plist's, etc. (anything that isn't a source file)
What you need to do is add those resources to your main project so that they are available for use inside your application, not the happiest way around it but it'll work.
As of Xcode 7 we can now use Asset Catalogs. In my experiments I've found that u can reference files in the same workspace bit different projects removing the need to micro manage assets in build phases or worrying about targets.

image referenced from a nib in the bundle with identifier "(null)" Error

There are several topics here with the same error.
The cause of my error is:
When I create my ViewController's instance by:
HomepageViewController *homePageViewController = [[HomepageViewController alloc] initWithNibName:#"HomepageViewController" bundle:nil];
Everything is fine. But I need localization.
So I am calling:
NSString *path= [[NSBundle mainBundle] pathForResource:[self language] ofType:#"lproj"];
NSBundle *languageBundle = [NSBundle bundleWithPath:path];
HomepageViewController *homePageViewController = [[HomepageViewController alloc] initWithNibName:#"HomepageViewController" bundle:languageBundle];
Then I cannot see the png images in the simulator getting "image referenced from a nib in the bundle with identifier "(null)" Error" What causes this error?
If you do localization in the recommended way, the code in your first code example should work as is. Behind the scenes, the localized .lproj folder will be examined and the XIB will be loaded from there if found. So you don't need to try to build a path to a localized .lproj folder yourself as you are doing in your second code sample!
I'm not sure if that's the cause of the issue you are having with png files, though. Have you verified that images are OK when you open the localized XIBs in XCode? Maybe one of your localized versions simply contains a reference to a file that has since been removed.

Resources