UIImage load wrong image in main bundle - ios

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.

Related

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

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];

iOS - UIImage imageNamed: returns null on device

[UIImage imageNamed:filename]
This method returns null only on the device.
I know it's known problem and it's usually due to the fact that simulator is case insensitive.
I have tried solutions proposed here: UIImage imageNamed returns nil
But nothing worked out for me.
The case is simple: I have 4 files named:Bar#2x~ipad.png, Bar#2x~iphone.png, Bar~ipad.png, Bar~iphone.png.
All of them are in project with target checkbox checked.
NSLog(#"%#",[UIImage imageNamed:#"Bar"]);
That line of code gives me null for device and I really have no idea what I'm doing wrong right now.
I did have such a problem recently too.
After playing with filenames and paths sometimes it helps when you clean and rebuild your project.
I found myself in the same situation recently.
The solution in my case was adding the extension to the file name.
[UIImage imageNamed:#"Bar.png"]
Completely clean your build and redo it:
delete the app from the device
option-clean the whole build directory in Xcode (⌘-Shift-K)
quit xcode
delete ~/Library/Developer/Xcode/DerivedData
restart xcode, build and run
This just happened to me, and to discover was very tough: I had one image which where nil just on device
logoWhite.png
my code:
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"LogoWhite"] forBarMetrics:UIBarMetricsDefault];
After a while of debugging, I noticed that the name of the image is beginning with capital case letter. Obviously this doesn't matter on OSX with a ignore case file system. iOs file system isn't, so the image worked on the simulator but not on the device.
I bet that all the solutions about cleaning derived data and rebuild did randomly end with renaming the image, and this would do the trick as well. Just posting here for future reference :)
I encountered this issue and just fixed it. I listed the solution below as a reference.
I have several images, and use [UIImage imageNamed:filePath] to show them. All of images displayed well except 1 on simulator/device, but all of them can be seen on OS X. For that image, [UIImage imageNamed] always return null.
After few minutes' investigation, I found that the image cause problem is far big in file size: 4.1M. Others are all around 800kb. They have nearly same size in dimension.
Then I try to open it in image editor, then re-save it. After this, the size dropped to 800k. problem solved.
The reasons I guess,
[UIImage imageNamed:filePath] has a max file size limit? (low possibility, but need to check official document)
the image itself has some error. But OS X has better tolerance than iOS. so iOS cannot read and return null. This issue is like OS X can play more types of video files than iOS since it support more codecs.
so if you encounter this issue in the future, take a few seconds look at the file size. Hope help.
This is an odd problem, which I hadn't seen until this week.
Below are my findings, and a workaround to your problem.
In my iPhone app, I download an image and store it locally, and this had always worked fine.
But now when I run the same code, it was suddenly failing to create a UIImage out of it using the imageNamed function, and now it was returning nil.
Three notes though:
This exact code did work before, using the same source code and .png image files. I'm not sure if my copy of XCode 6.x or iOS 8.x quietly updated itself in the meantime.
The code continues to work okay (with the same image file) on the iPhone simulator. It just doesn't work on a real device.
Take a look at the code below. When UIImage:imageNamed failed, I ran some code to check if the file actually existed.. and it did. Then I loaded the binary data from the file using NSData:contentsAtPath (which also proves that the file exists and was in the right folder), then created a UIImage out of that, and it worked fine.
Huh ?!
UIImage* img = [UIImage imageNamed:backgroundImageFilename];
if (img != nil)
{
// The image loaded fine (this always worked before). Job done.
// We'll set our UIImageView "imgBackgroundView" to contain this image.
self.imgBackgroundView.image = img;
}
else
{
// We were unable to load the image file for some reason.
// Let's investigate why.
// First, I checked whether the image was actually on the device, and this returned TRUE...
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:backgroundImageFilename];
if (fileExists)
NSLog(#"Image file does exist.");
else
NSLog(#"Image file does not exist.");
// Next, I attempted to just load the bytes in the file, and amazingly, this also worked fine...
NSData *data = [[NSFileManager defaultManager] contentsAtPath:backgroundImageFilename];
if (data != nil)
{
// ..and then I COULD actually create a UIImage out of it.
img = [UIImage imageWithData:data];
if (img != nil)
{
// We have managed to load the .png file, and can now
// set our UIImageView "imgBackgroundView" to contain this image.
self.imgBackgroundView.image = img;
}
}
}
As I said, this code does provide a workaround for this problem, but it's very odd that it's suddenly started happening.
And, I should say, I did try the other suggestions in this thread, cleaning the project, removing the DerivedData, completely removing the app from the device, and so on, but they didn't make any difference.
I would be interested in knowing if anyone else hits this issue, and finds that my code sample works for them.
Update
I'm an idiot.
I'm not sure if the UIImage:imageNamed function has changed or something (and if so, why it continues to work okay on the iPhone 8.1 Simulator), but I found the following one line does work okay:
UIImage* img = [[UIImage alloc] initWithContentsOfFile:backgroundImageFilename];
So it seems as though you should use this function for loading images which aren't a part of your app's bundle.
I also have same issue then : XCode - Build Phases - Copy Bundle Resources -{see image is available or not}- add{image} - clean - delete app - Run .
I would like to add one more important point to all the above solutions
Make sure you add your image resource to right target
By mistake if Developer mess-up link with resource and target then conditions arise.
if you have multiple target then double check the resource are set to correct target.
Attached screenshot example in case of resource link between multiple targets.

Oganization of Folders in XCode

I'm using physical folders to organise my files in XCode, as I have multiple files with the same name. Eg: /skin1/navbar.jpg and /skin2/navbar.jpg.
Every time I add a new file, I can't drag it into the folder, it seems like once I ad the folder as a group in XCode, it gets locked somehow and I can't change it. When I try to initialize a UIImage with this method, it turns out to be nil.
Using this image as an example:
To create a UIImage I'm using this:
NSString *skinBasePath = [AppearanceUtils skinBasePath]; // Returns /Users/echilon/Library/Application Support/iPhone Simulator/5.1/Applications/E6C7ECF4-B36A-4092-97BC-CDE2D0A526C2/ReadMeAStory.app/ReadMeAStory/images/skin/wood/
NSString *bgImagePath = [skinBasePath stringByAppendingPathComponent:#"button_181_43.png"],
*bgImagePathPressed = [skinBasePath stringByAppendingPathComponent:#"button_181_43_pressed.png"];
UIImage *bgImage = [UIImage imageWithContentsOfFile:bgImagePath]
Everything looks fine in the debugger until I actually create the image, which comes back as null.
The full path to the image in the debugger is /Users/echilon/Library/Application Support/iPhone Simulator/5.1/Applications/E6C7ECF4-B36A-4092-97BC-CDE2D0A526C2/ReadMeAStory.app/ReadMeAStory/images/skin/wood/button_181_43.png.
What am I missing?
"images" is just a Group and not a Folder, so .../ReadMeAStory.app/ReadMeAStory/images/ don't exist.
What are you doing in [AppearanceUtils skinBasePath]?
Maybe this will help:
NSString *path=[[NSBundle mainBundle] pathForResource:#"button_181_43" ofType:#"png" inDirectory:#"skin/wood"];

NSBundle creates files for given path

When I use
NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:#"sqlite"];
The NSBundle class, instead of returning nil, for a given file name when the file is not included in Xcode, seems to be creating the file no matter what. I tried removing file from xcode, it did not work, then I went to the path for simulator for the project (that path is generated by pathForResource) :
/Users/USER_NAME/Library/Application
Support/iPhoneSimulator/6.0/Applications/APP_CODE/APP_NAME.app/sampleDB.sqlite
Before I run program in the debugger, I delete file "sampleDB.sqlite" manually. Every time the method pathForResource is called the file seems to magically reappear in the folder.
The documentation states :
Return type :
The full pathname for the resource file or nil if the file could not be located.
EDIT:
With a pathForResource commented out, files removed - I still get that file magically reappearing in the bundle. Any Ideas?
Make a clean build to make sure that the file gets removed from the app bundle.

How to make an iOS asset bundle?

I saw a custom asset bundle in an iOS project I evaluated, so at least I know it's possible.
My issue is that I'm using a CATiledLayer with about 22,000 tiles for a given image and it takes a very long time to compile (half an hour clean build, 5-10 minutes for regular build). So, I want to take all of the images and make a custom bundle to make it portable and hopefully not recompile into the app bundle each time.
How do I go about this? I checked the docs, but didn't see an explanation on how to actually create the bundle.
Answer is stupidly simple
Make a folder in finder, add files to it, rename it to bundlename.bundle
drag into Xcode - success!
to access, use the form of PathToMainBundle+"/bundlename.bundle"
How to create a bundle
Create a folder in finder.
Add files to the folder
Rename the folder so that its extension is .bundle (e.g. "New folder" -> "BundleName.bundle")
PS: You can at any time right click the folder and hit "Show package content" in order to add, remove or modify any of the files.
How to add the bundle to Xcode
Drag it into Xcode
How to use the bundle
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"BundleName" ofType:#"bundle"];
NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
NSString *resource = [bundle pathForResource:#"fileName" ofType:#"fileType"];
(Replace BundleName, fileName and fileType with appropriate names)
Two other helpful bits of advice:
First, in order to see the contents of the bundle in Xcode you need to set its type in the File Inspector Utility Pane, to "Application Bundle". You still won't be able to copy to and from via Xcode. You'll need to use Terminal but Xcode will update it immediately.
Second, in order to use resources in the bundle here's a helpful snippet...
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"AquarianHarp" ofType:#"bundle"];
NSString *imageName = [[NSBundle bundleWithPath:bundlePath] pathForResource:#"trebleclef2" ofType:#"png"];
UIImage *myImage = [[UIImage alloc] initWithContentsOfFile:imageName];
As mentioned in my comment above, you needn't actually load the bundle (you can't as it's not executable) and the ofType needs to match the case of your actual file for it to work on the device. It will work either way in simulator so don't be fooled by this red herring!
Finally, you don't need to put your resources in the "Resources" subfolder inside the bundle. It seems you can use an arbitrary layout but there may be unknown performance implications.
Here's how I got this to work: In Xcode create a new file | Resource | Settings Bundle. Then in the Finder select that bundle and choose Show Package Contents, and add whatever image files.
Then in the code reference an image this way:
NSString *imgName = #"bundlename.bundle/my-image.png";
UIImage *myImage = [UIImage imageNamed:imgName];
Here are the steps to create an asset or resource bundle (ex. FrameworkResources.bundle) - it's surprisingly non-obvious. This is especially useful if you are creating static frameworks.
Press File -> New -> Target in Xcode
Select "macOS" tab, search "Bundle"
Tap "Bundle" -> click "Next" -> type product name "MyResources" -> tap "Finish"
Go to "Build Settings" for the newly created Bundle. Change "Base SDK" (SDKROOT) to "iOS'
Go to "Build Phases" for the newly created Bundle. Delete "Compile Sources" and "Link Binary With Libraries" (this will remove the executable within the bundle which can cause all sorts of build and app submission errors)
Creating a loadable bundle project is just like creating an application—you just need to pick the appropriate project template. To create a loadable bundle project, perform the following steps:
Launch Xcode.
Choose New Project… from the File menu.
From the template list, select Cocoa Bundle.
Click Next.
Choose the location for the project and click Finish.
Build and run, in Xcode you will see the bundle file in your Products folder of Project Navigator. Right-click the bundle and select "Show in Finder".
My notes on bundling and reading files in an Xcode project
Steps:
Create a test.txt file and add the text "testing" to it then put it in a folder named test.bundle
Drag and drop it next to your .app file in Xcode (copy)
print(Bundle.main.resourcePath!+"/temp.bundle/test.txt") Output: /Users/James/Library/Developer/Xcode/DerivedData/GitSyncMac-heiwpdjbtaxzhiclikjotucjguqu/Build/Products/Debug/GitSyncMacApp.app/Contents/Resources/temp.bundle/test.txt
Example:
print(content(Bundle.main.resourcePath!+"/temp.bundle/test.txt")) // testing
static func content(_ path:String)->String?{
do {
let content = try String(contentsOfFile:path, encoding:String.Encoding.utf8) as String//encoding: NSUTF8StringEncoding
return content
} catch {
return nil
}
}

Resources