jpg images in iphone and 2x images - iphone

I am working on an iphone app and targeting iOS 4.0 or later. I am trying to add an image to UIImageView, and image is in jpeg format. This is what I have done so far..
UIImageView *bgImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 246.0)];
bgImageView.image = [UIImage imageNamed:#"background"];
[self.view addSubview:bgImageView];
[bgImageView release];
I have added two images,
background.jpg (as normal 1x image)
background#2x.jpg (for 2x / retina
display).
But when I build and run, everything runs fine, except there is no background. All my other widgets are there, but there is a white background and image is not showing.
Next I created two png images for my background, added it to the project and ran. This time my background was shown. I was perplexed because according to documentation jpg image can be used in iphone with UIImage.
I tried again with my jpg images and changed the code above to this
UIImageView *bgImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 246.0)];
bgImageView.image = [UIImage imageNamed:#"background.jpg"]; //added extension
[self.view addSubview:bgImageView];
[bgImageView release];
When I run the above code, background image is showing, but 2x images are not loaded. That shows my jpg image is fine, and it is not the culprit. So when I just give image name without extension, png files (both 1x and 2x)are loaded on respective devices, jpg files are not loaded at all.
Looks like a bug (either in sdk or documentation). iOS 4 is here for a good year. why no one noticed it? Also is there any option for me other than to convert all my images to png?
EDIT :
Well, two days in to the question, zero answers. I have already converted all my images to png, and that part of my project is done (as my project couldn't wait..). Still, for my own sake and for sake of everyone who faces this in future, I would like to know more about this problem. At least someone can confirm that this is indeed an apple bug. I am starting a bounty so that more people can see this thread.
EDIT AGAIN
2 days remaining for this bounty to end. Thanks for everyone who responded. But let me clarify one thing. I have already said that I have converted all my images to png and I moved along. I just wanted some confirmation that imageNamed treats jpg and png files same way. Documentation certainly says it will(unless Apple has written it somewhere other than UIImage documentation). If documentation is correct then my code failed me because of two possible reasons
It is a bug in imageNamed. If this is the reason, then I dont have too much option other than file a bug report, change my images to png or write a wrapper(like Chandan has said in his answer) and move on..
There is some problem with the jpeg image I used. Well I am not a photoshop guy. But I used
RGBA jpg image. If image does matter I am ready to ask my designer
friend and provide more info.
One more thing. This post also just tells about this problem. But he dont know the reason behind. So I am not alone.
EDIT : THE END
Well it is something related to the image itself. I googled and downloaded some sample jpeg images and played with it. Some of them shown up correctly and some doesn't. Since the boundy time is up, I am giving the boundy to PengOne who tried to reproduce the bug and successfully loaded jpg image without extension, there by making me to work with more images. Thanks for everyone who tried to help.

According to the UIImage Class Reference:
Discussion
This method looks in the system caches for an image object with the
specified name and returns that object if it exists. If a matching
image object is not already in the cache, this method loads the image
data from the specified file, caches it, and then returns the
resulting object.
On a device running iOS 4 or later, the behavior is identical if the
device’s screen has a scale of 1.0. If the screen has a scale of 2.0,
this method first searches for an image file with the same filename
with an #2x suffix appended to it. For example, if the file’s name is
button, it first searches for button#2x. If it finds a 2x, it loads
that image and sets the scale property of the returned UIImage object
to 2.0. Otherwise, it loads the unmodified filename and sets the scale
property to 1.0. See iOS Application Programming Guide for more
information on supporting images with different scale factors. Special
Considerations
On iOS 4 and later, the name of the file is not required to specify
the filename extension. Prior to iOS 4, you must specify the filename
extension.
Since you're targeting iOS 4.0 and later, you should not need the filename extension. I tried to reproduce this potential bug, but it worked as expected for me without the filename extensions.
Here are some ideas for what may have gone wrong to create this effect in your app:
It's possible that the problem comes from the cache if you changed your images at some point.
If you choose not to use the filename extensions, and you have both "background.jpg" and "background.png" as options, the preference appears to be to use the ".png" file.
If you set the target to iOS 4.0 and later after first running the app, then the filename extension would have been required and the image may have been cached blank, which leads back to theory 1.
That's the best I can make of this.

Write a wrapper to get the image like
-(UIImage*) getImage:(NSString*)imageName{
UIImage *image;
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)] == YES && [[UIScreen mainScreen] scale] == 2.00) {
// RETINA DISPLAY
NSString *jpegFile = [imageName stringByAppendingString:#"#2x.jpg"];
image = [UIImage imageNamed:jpegFile];
}
else{
NSString *jpegFile = [imageName stringByAppendingString:#".jpg"];
image = [UIImage imageNamed:jpegFile];
}
return image;
}
And from your code call
bgImageView.image = getImage(#"background");

With the dawn of Xcode 6 I have found that JPG images might not render at all on certain iOS7 devices (they may work on some but not on others, or they may not work on any, and you may see a different result on a different developers machine, there doesn't seem to be any consistency). This is regardless of whether the image is just referenced in UIImageView in a XIB or programmatically with [UIImage imageNamed:] (which returns nil).
After scratching my head for a long time, the solution was to rename the *.jpg file extension to *.png (whilst obviously leaving the file contents as JPG data).
HTH

You just need to provide the full name of image.
bgImageView.image = [UIImage imageNamed:#"background.jpg"];
don't leave off the extension.
As for the #2x, it is not necessary to call it out anywhere in the code. If you code correctly there is no need to test for a retina display as some here have suggested.

You can use jpgs, just tried it myself and it worked. Only suggestion is how you are instantiating it.
Try it this way:
stockImage = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"Morbo.jpg"]];
stockImage.frame = CGRectMake(0,0, self.view.bounds.size.width, self.view.bounds.size.height) ;
[self.view addSubview:stockImage];
This is the image I used:
http://futurama.wikia.com/wiki/File:Morbo.jpg

Related

How to load a large local image (works on simulator, not test device)?

I'm currently trying to load a map image which is a large(16mb) .jpg file inside a scrollView so you can zoom in and out.
When I launch the app inside the simulator everything runs fine and smooth. However once I run it on my test device (iPod 4.1, iOS 6.0) the app shows the launch image and then it crashes with no error messages at all.
This is how the code currently looks like.
myImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"map.jpg"]];
myScrollView=[[UIScrollView alloc]initWithFrame:CGRectMake(15,120,290,365)];
myScrollView.showsVerticalScrollIndicator=YES;
myScrollView.scrollEnabled=YES;
myScrollView.maximumZoomScale = 10.0;
myScrollView.minimumZoomScale = [myScrollView frame].size.width / myImage.frame.size.width;
myScrollView.clipsToBounds = YES;
myScrollView.bounces = NO;
myScrollView.showsHorizontalScrollIndicator = NO;
myScrollView.showsVerticalScrollIndicator = NO;
[self.view addSubview:whiteFrame];
[self.view addSubview:myScrollView];
myScrollView.contentSize = CGSizeMake(myImage.frame.size.width,myImage.frame.size.height);
myScrollView.delegate = self;
myScrollView.zoomScale = [myScrollView frame].size.width / myImage.frame.size.width;
[myScrollView addSubview:myImage];
Any help is greatly appreciated.
EDIT:
I found this in the docs
If you have a very large image, or are loading image data over the web, you may want to create an incremental image source so that you can draw the image data as you accumulate it. You need to perform the following tasks to load an image incrementally from a CFData object:
Create the CFData object for accumulating the image data.
Create an incremental image source by calling the function CGImageSourceCreateIncremental.
Add image data to the CFData object.
Call the function CGImageSourceUpdateData, passing the CFData object and a Boolean value (bool data type) that specifies whether the data parameter contains the entire image, or just partial image data. In any case, the data parameter must contain all the image file data accumulated up to that point.
If you have accumulated enough image data, create an image by calling CGImageSourceCreateImageAtIndex, draw the partial image, and then release it.
Check to see if you have all the data for an image by calling the function CGImageSourceGetStatusAtIndex. If the image is complete, this function returns kCGImageStatusComplete. If the image is not complete, repeat steps 3 and 4 until it is.
Release the incremental image source.
Does anyone know about a sample code for that ?
A 16mb jpg is going to take a pretty decent chunk of RAM when uncompressed. Your app is probably crashing because it is using too much memory. You can check this by reproducing the crash and checking the device's console log in the Xcode organizer.
You will need to either:
Reduce the dimensions of the image as much as your design will permit to greatly reduce memory usage. For example, there's no reason to have a 16mb jpg if it will never be shown at full-resolution.
Chop the image up into tiles and only load the tiles currently displayed on the screen and the surrounding areas. Then load additional tiles as the user scrolls around. This is how maps apps are able to display extremely large images without running out of RAM.
Remember to also test your app on the supported device with the lowest amount of RAM. These devices will probably kill your app sooner than the newest devices.
At 16 MB, the image is too big for your iPod to handle. The app is crashing because it's loading the image directly, and asking for too much memory and the system has had to kill it.
You should create a smaller version of your image, in terms of both canvas size and image quality, for your app.
You could also incrementally load the image instead.
When you're testing on the simulator, it has full access to the gobs of memory on your computer. Way more than the 256MB available to your iPod 4 (there isn't such thing as an iPod 4.1).
Have a look at CATiledLayer
https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CATiledLayer_class/Introduction/Introduction.html
There is also an example Apple project using it
https://developer.apple.com/library/ios/samplecode/PhotoScroller/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010080
Good luck
I hope you are not loading this image on app launch. Application launch should be as light as possible. If launch time is more than 20 secs then iOS kills the app on its own. On simulator resources are much bigger and you may not see the similar behavior as that with device. You may also want to use the lazy loading technique to load lighter image first and then load heavy image only when needed while zooming, panning etc.
Having said that, one imortant aspect of [UIImage imageNamed] is that it caches all images loaded in that way and they never get unloaded, even if you dealloc the UIImage that was created! This is good in some circumstances (e.g. smallish images in UITableView cells), but bad in others (e.g. large images).
The solution is to use [UIImage imageWithData] which does not do this caching and which unloads the data when the UIImage is dealloc'd.
Another solution to this problem consists of either loading scaled version of the image (*1) or you have option to display it in full resolution with help of CATiledLayer(*2).
[UIImage imageWithCGImage:scale:orientation:];
CATiledLayer example

Images in table view are getting disappear after scrolling for logn time

My application have UITableView. I am using custom cell that have lable and UIImage. Its working fine for normal scrolling. But if I scroll that for long time, then images get disappeared. And there was error = 24 (to many open files) in console.
From Google, I got that instead of using UIImage imageNamed, used initWithContentOfFile. But that dose not work. Please help.
Edit Adding code written in comments:
This is the code used for adding the image to the cell:
UIImage *img =[[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"sms_icon" ofType:#"png"]]; [cell.img setImage:img]; [img release];
The documentation says
The method (+ImageNamed:) looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method loads the image data from the specified file, caches it, and then returns the resulting object.
On a device running iOS 4 or later, the behavior is identical if the device’s screen has a scale of 1.0. If the screen has a scale of 2.0, this method first searches for an image file with the same filename with an #2x suffix appended to it. For example, if the file’s name is button, it first searches for button#2x. If it finds a 2x, it loads that image and sets the scale property of the returned UIImage object to 2.0. Otherwise, it loads the unmodified filename and sets the scale property to 1.0.
On iOS 4 and later, if the file is in PNG format, it is not necessary to specify the .PNG filename extension. Prior to iOS 4, you must specify the filename extension.If you have an image file that will only be displayed once and wish to ensure that it does not get added to the system’s cache, you should instead create your image using imageWithContentsOfFile:. This will keep your single-use image out of the system image cache, potentially improving the memory use characteristics of your app.

App failing to see #2x images

This is driving me nuts. Been searching for 2 days, and I can't find any real solution or explanation for why this is happening. I know there are threads here on SO, as well as some other places, but they have been no help. I have read the Apple documentation on the matter.
I have normal and #2x images in my app. They are named correctly (edit_image.png, and edit_image#2x.png). They are sized correctly (normal is 60x60, #2x is 120x120). They are both being copied into the app bundle, and when I examine the contents, I can see them both in the root.
I am grabbing the image by calling [UIImage imageNamed:#"edit_image"]. It never grabs the 2x image. It only sees the 1x image. However, if I check the scale first, and use this code:
if ([[UIScreen mainScreen] scale] == 1) {
NSLog(#"test");
editImage = [UIImage imageNamed:#"edit_image"];
} else {
editImage = [UIImage imageNamed:#"edit_image#2x"];
}
Then it does grab the correct image. I have tried everything. I deleted the high res from the project, cleaned, re-added the high res, cleaned and then built, no dice. I deleted all the images, and re-added them, no dice. I have done everything I can think of. What the hell is going on here?
Are you creating universal application for both iPhone & iPad. If universal app is there then you need to create 3 set of images:
1) edit_image~iPad.png
2) edit_image~iphone.png
3) edit_image#2x~iphone.png
each with the same resolution of 72 pixels/inch. While you need to provide double size for that #2x image which I think you've already done this.
Now, try the below code
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"edit_image" ofType:#"png"];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
Important: When creating
high-resolution versions of your
images, place the new versions in the
same location in your application
bundle as the original.
Source: http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/SupportingResolutionIndependence/SupportingResolutionIndependence.html
Two silly mistakes that I've made that can cause this problem:
Accidentally naming the small
versions #2x instead of the large
ones
Having the large versions be
slightly missized (by one pixel)
You don't need to add the "#2x" bit, or have the if-else logic at all. Just use [UIImage imageNamed:#"edit_image"].
I was suffering from the exact same problem, and finally found a solution (after 2 days of searching).
In my case the name of the #2x image didn't exactly match the normal sized image: tileSet.png and tileset#2x.png.
What made this so difficult to discover is that the #2x file did have the correct name in Finder and in XCode. I was only able to discover the problem by opening the image in Preview and looking at the Inspector. I don't know enough about the Mac file system to explain how this happens, but once I renamed the file to gibberish, then renamed it back to tileSet#2x.png everything started working as expected.

Hi-Res #2x image not being picked up for tab bar item

I have a TabBarController that sets the image for the tab like so, in the -init method:
self.tabBarItem.image = [UIImage imageNamed:#"tabImage.png"];
I have a tabImage#2x.png file in the resource. In the iPhone 4 simulator or the phone, the hi-res image isn't being picked up - the low res version is simply being scaled up.
Any ideas why this might be?
EDIT: Some more info:
If I try and explicitly use tabImage#2x.png (or just tabImage#2x) then the tab image I see is extremely large and blown up beyond the bounds of the tab, as if it's being scaled from 60px to 120px. So it looks like whatever name is supply is being treated as a scale=1.0 image.
Note that the simulator is not case-sensitive, but the device is. Make sure case matches EXACTLY. If you've changed the case of the filename at some point, you'll need to clean and rebuild. Sometimes, for the simulator, I've had to actually blow away the folder in Library/Application Support/iPhone Simulator/4.3/Applications/ to get the rebuild to pick up the renamed image.
Always use
[UIImage imageNamed:#"foo.png"]
This will work on 3.x and 4.x devices, and on the 4.x Simulator. Devices with Retina Displays (and the 4.x simulator) will magically pick up the #2x versions of your images; iOS has been modified to be smart about this function and #2x.png files.
Make sure you have both the #2x.png and the normal.png added to the project file, and do a full clean & build. As others have mentioned, verify the size of the images, too; apparently if they're not exactly 2x the dimensions it won't work (I haven't verified this myself).
If you leave the .png off, it will only work on iOS 4.0. So if you're building a 4.0+ only app, you can ask for:
[UIImage imageNamed:#"foo"]
If you have only one hi-res image and want to use it on both Retina and non-Retina devices, then you'll have to change view.contentMode to scale to fit.
I had the same problem. It turned out that my png was not square. Solution: make it square and it will work.
Are you sure the file has been added to the XCode project and is visible in the project explorer?
I had this problem as well.
Make 2 images:
30x30 pixels
60x60 pixels
Suffix the 60x60pixel image with #2x. For example, tabBarImage#2x.png. Then, in your storyboard or code, you can specify the regular one, tabBarImage.png, and iOS will choose the #2x version at its discretion.
You can leave the .png off now. I believe it will still work, but you may try that.
I just went through a few hours of redoing art in The Gimp and trying to get it recognized and loaded by my app on an iPhone 4.
I ran into the problem described with certain images with a #2x extension not being recognized and loaded.
I was not able to discern any pattern. My images are all loaded using [UIImage imageNamed:#"<name>.png"] into a singleton. I inspected the image scale settings post-startup and some were 1.0 (the old art) and some were 2.0 (the new art).
The only way I was able to resolve this problem was to delete and re-add the high resolution images that were not being recognized.
Two silly mistakes (both of which I've made before) that can cause this problem:
Accidentally naming the small
versions #2x instead of the large
ones
Having the large versions be
slightly missized (by one pixel)
you need 2 versions of your images and both ned to be at the same location in the project folder and added to the project
image.png 60x60
image#2.png 120x120
then simply use [UIImage imageNamed:#"image.png"]
did it this way with selfmade buttons and it worked for me (iOS 4.1)
Another thing to look out for is having two images with the same name.
I had the same issue. The #2x image had the wrong build target checked (ServiceTests instead of MyProject).
I had exactly the same problem.
Make two images: im1.png and im1#2x.png
Call imageNamed: with the first one.
Note, imageNamed: doesn't initialize UIImage, hence use it as transient [[UIImageView new] initWithImage:[UIImage imageNamed: #"im1.png"]] or initialize UIImage yourself.

iphone 4 - loading images x old devices

Apple says you must have #2x versions of your images and use a stuff like
UIImage* anImage = [UIImage imageNamed:#"Button"]; // without the extension
That will select the lowres and hires versions of Button image, depending on the iPhone version (3G/3GS or 4). But what if the app is for iPad too?
Will the iPad load the Button image even if I don't specify the extension?
thanks.
Will the iPad load the Button image even if I don't specify the extension?
No. From the doc:
Special Considerations
On iOS 4 and later, the name of the file is not required to specify the filename extension. Prior to iOS 4, you must specify the filename extension.
Since iPad is running 3.2, you need to include the .png part. However, writing
UIImage* anImage = [UIImage imageNamed:#"Button.png"];
will not prevent the system from finding Button#2x.png on 4.0.
Either
UIImage* anImage = [UIImage imageNamed:#"Button"]; // without the extension
or
UIImage* anImage = [UIImage imageNamed:#"Button.png"]; // with the extension
will work on iOS4, since apple basically discourage you from using any other image than PNG you can omit the exertion.
Both work for #2x.png files as well. Make sure the #2x files have been added to your project.
as a complement to the answer, I must mention this page:
the problem is that the SDK is broken and despite Apple mentioning that other methods beyond imageNamed load the #2x images, they don't. Shame on Apple to make us lose time when their SDK is broken.
the mentioned link has the solution for the problem, using a way around.
Yes, it will load the standard (lores) version.
Edit: Don't forget to test on the simulator or real device, as there are some twiddles, but imageNamed: has proven to work well so far.