iPhone: Image View picture size limit - iphone

Hey guys first I got a picture into a image view that was 2900x2100 and the file size was about 4MB I also had an scroll view to move around the image.
Anyways it crashed, first I though it was because the 4MB size. Then I resized it making it 1287x1234 and 1.3MB but it was pixelated when I zoom it too much.
After that I manage to make it 2900x2100 with 1.5 MB! so I ran it on my device and it still crashes!!
Anybody knows why's that?
This is what I get on the debugger console
Program received signal: “0”.
warning: check_safe_call: could not restore current frame
here's more info, this is my viewDidLoad method:
UIImageView *tempimage = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"sanlucasFLAT.jpg"]];
self._FLatLucasImageView = tempimage;
[tempimage release];
_FLatLucasImageView.frame = CGRectMake(-7, 0, 800, 800);
_FLatLucasImageView.contentMode = UIViewContentModeScaleAspectFit;
_FlatLucasScrollView.contentSize = CGSizeMake(_FLatLucasImageView.frame.size.width,_FLatLucasImageView.frame.size.height);
_FlatLucasScrollView.delegate = self;
_FlatLucasScrollView.clipsToBounds = YES;
[_FlatLucasScrollView addSubview:_FLatLucasImageView];
[_FLatLucasImageView release];
When I set the CGRectMake to: (0,0,2900,2961) it doesn't crash when I scroll the image
Best Regards
Carlos Vargas

The reason why your app crashed is because the picture you are using is too big.
According to the class reference for UIImage, it says that You should avoid creating UIImage objects that are greater than 1024 x 1024 in size.
If you add a UIImage object which has a bigger image than 1024 x 1024 to a UIImageView object, your app could crash. If you are lucky, sometimes your app doesn't crash though.

The file size of your image doesn't really matter here: UIImage will have to decompress it and store the decompressed image in memory. It would usually need 4 bytes for each pixel, which means a 2900 x 2100 pixel image will take up more than 24 MB of memory. On an iPhone or iPhone 3G you might not even have that much memory available for your whole program.
Maybe try splitting it up into smaller images and then load them as necessary when the user scrolls around your view. Just cut it into tiles that are about 320x480 or so (the iPhone's screen size) and then only add the visible tiles to a UIScrollView, and remove them when the user scrolls away and they become invisible.

This code is very messy. Couple of things:
Try not to use instance variables with an underscore prefix (apple semi-private)
Try not to use instance variables start with a uppercase letter (convention, things start start with an uppercase letter are usually classes or types)
Do not define properties for instance variables that you do not actually expose. If they are simply things internal to your class then you do not need a property.
That being said, my guess is that you are over-releasing the image view. Try removing that last [_FLatLucasImageView release].
If you do not need to access that image view after you have added it to the scroll view then I suggest to completely remove the instance variable and property and simply have code like this:
UIImage* image = [UIImage imageNamed: #"sanlucasFLAT.jpg"];
if (image != nil)
{
UIImageView* imageView = [[[UIImageView alloc] initWithImage: image] autorelease];
if (imageView != nil)
{
imageView.frame = CGRectMake(-7, 0, 800, 800);
imageView.contentMode = UIViewContentModeScaleAspectFit;
flatLucasScrollView.contentSize = CGSizeMake(imageView.frame.size.width, imageView.frame.size.height);
flatLucasScrollView.delegate = self;
flatLucasScrollView.clipsToBounds = YES;
[flatLucasScrollView addSubview: imageView];
}
}

Related

Have UIImage fill the whole frame of the UIImageView

I am working on a small game project, and i have created an image that is 25 x 35 Width / height. Now, if i create a UIImageView with a frame that exactly matches the image, and set the image of that object to the image named above, i thought it would fill it all, but it doesn't, not even by a long shot. It's like there is this huge rectangle, and the image is just added in the middle, so it only fills like 50% of it? any way i can make the image fit exactly?
I tried making the background with a UIColorWithPatternImage, but still doesn't work.
Thanks on advance
/JBJ
Try this:
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.image = image;
imageView.frame = frame;
[imageView sizeToFit];
if you have a 25x35 image and 25x35 UIImageview everything must be ok.
Please post some line of code.
If you want to make a game have considered engine like cocos2d? (http://www.cocos2d-iphone.org/)
imageView.image = image;
imageView.bounds = CGRectMake(0,0,image.size.width,image.size.height);

Fetching images from server while drawing the cell

Why should we avoid loading a image from server while drawing a cell?
In my app I get data from server which contains image URLs as well. Now, I draw my cells and use these image URLs to fetch image from the server and draw it. Now, sometimes image does not get displayed even if image is actually present at that URL as I can see it through browser.
Is there any cell drawing limitation which could cause this issue? Shall I fetch images when I get data from server.
Does cell rendering happens before image is actually drawn.
NSString *imgUrl = [ImageURLArray objectAtIndex:indexPath.row];
if(imgUrl != nil)
{
NSString * ImagePath;
NetworkManager *manager = [[NetworkManager alloc] init];
ImagePath = [manager GetFile:imgUrl];
[manager release];
manager = nil;
if(ImagePath != nil)
{
UIImage *newImage = [UIImage imageWithData:[NSData dataWithContentsOfFile:ImagePath]];
UIGraphicsBeginImageContext(CGSizeMake(50, 50));
// now redraw our image in a smaller rectangle.
[newImage drawInRect:CGRectMake(25, 10, 30, 30)];
newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
cell.imageView.image = newImage;
}
}
return cell;
}
hope this will solve your problem
What could be happening is you have a main thread which is the UI display thread. By fetching image from a url from this thread, you are essentially blocking the cell (i.e. scrolling). Also might be the case that the image is in the process of downloading in which case you'll not see it till it downloads.
Generally UITableView does not display the cell till it's ready to be displayed, by doing the image download in the main thread you are harming your performance. What you should do is to launch a background thread which downloads the image from the url & then update the cell of the image contents when the download is ready.
Best way is to have an initial placeholder like a default image or a spinner, which gets replaced when the main image download gets done.
Dont worry you dont need to implement all this. No point in reinventing the wheel. Here's an awesome library which I use for the same - UIImageView + WebCache
fetch images when I you data from server and pass the information to the view.
like
ImageView *imageView = [[ImageView alloc] initWithNibName:#"ImageView" bundle:[NSBundle mainBundle]];
imageView.prod=self.prod;
imageView.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:imageView animated:YES];
[imageView release];
imageView=nil;
hope this will solve your problem
Clearly, the image download is taking more time. You should be using placeholder images until the download completes. You can't stop the user from scrolling.

3D Rotation of Object

I am trying to rotate my object like shakes dice.
please suggest simplest way to implement it in my iphone application.
Any kind of sample code or documentation.
http://www.amazon.com/OpenGL-SuperBible-Comprehensive-Tutorial-Reference/dp/0321498828
If you want to do this without using OpenGL you can use a UIImageView and the set a series of animation images. By creating a series of images you can create a "rolling dice" effect. Here is an example of how to do this:
// create a UIImageView
UIImageView *rollDiceImageMainTemp = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"rollDiceAnimationImage1.png"]];
// position and size the UIImageView
rollDiceImageMainTemp.frame = CGRectMake(0, 0, 100, 100);
// create an array of images that will represent your animation (in this case the array contains 2 images but you will want more)
NSArray *savingHighScoreAnimationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"rollDiceAnimationImage1.png"],
[UIImage imageNamed:#"rollDiceAnimationImage2.png"],
nil];
// set the new UIImageView to a property in your view controller
self.viewController.rollDiceImage = rollDiceImageMainTemp;
// release the UIImageView that you created with alloc and init to avoid memory leak
[rollDiceImageMainTemp release];
// set the animation images and duration, and repeat count on your UIImageView
[self.viewController.rollDiceImageMain setAnimationImages:savingHighScoreAnimationImages];
[self.viewController.rollDiceImageMain setAnimationDuration:2.0];
[self.viewController.rollDiceImageMain.animationRepeatCount:3];
// start the animation
[self.viewController.rollDiceImageMain startAnimating];
// show the new UIImageView
[self.viewController.view addSubview:self.rollDiceImageMain];
You can modify the creation and setup including the size, position, number of images, the duration, repeat count, etc as needed. I've used this feature of UIImageView to create simple animation effects and it works great!
Please let me know if you try this and if it works for your needs. Also, post any follow up questions.
Bart
nice your example, but do you think it's possible to rotate / control the rotation with touch gesture ?

Animation one image to the next

I have a flash file and need to reprogram for iPhone.
The design of the app is a navbar with start buttom and an image view below them. I have 46 PNL images that I want to show in succession after the start button is clicked. Each picture will stay on the screen for 5 seconds.
I tried to replicate a code that I got off of YouTube but it did not work.
For the viewcontroller.h I used the following code verbatim and was able to link images (I call them ac) to the image view and also to establish a link for the start button:
{
IBOutlet UIImageView *ac;
}
-(IBAction)startclick:(id)sender;
For the viewcontroller.m I used the following concept but I received many syntax warnings:
NSarray
List of 46 png files using #" notation for string
Last png followed by nil
Then some notation for length that each image appears.
If someone could help me out with the viewcontroller.h and viewcontroller.m to command this sort of animation, it would be much appreciated.
You should use UIImageView's animationImages property to do this, with your button calling startAnimating and/or stopAnimating:
UIImage *frame1 = [UIImage imageNamed:#"frame1.png"];
UIImage *frame2 = [UIImage imageNamed:#"frame2.png"];
UIImage *frame3 = [UIImage imageNamed:#"frame2.png"];
UIImage *frame4 = [UIImage imageNamed:#"frame2.png"];
uiImageView.animationImages = [[NSArray alloc] initWithObjects:frame1, frame2, frame3, frame4, nil];
uiImageView.animationDuration = 1.0 // defaults is number of animation images * 1/30th of a second
uiImageView.animationRepeatCount = 5; // default is 0, which repeats indefinitely
[uiImageView startAnimating];
// [uiImageView stopAnimating];
If you can't work out the syntax of Objective-C, you're going to struggle to do pretty much anything related to iPhone development.

Problem dealloc'ing memory used by UIImageViews with fairly large image in an UIScrollView

I have a large UIScrollView into which I'm placing 3-4 rather large (320x1500 pixels or so) UIImageView image tiles. I'm adding these UIImageViews to the scroll view inside of my NIB files. I have one outlet on my controller, and that is to the UIScrollView. I'm using a property (nonatomic, retain) for this, and sythesizing it.
My question is this: When I observe this in Memory Monitor, I can see that the memory used goes up quite a bit when the view with all these images is loaded (as expected). But when I leave the view, it and its controller are dealloc'd, but do not seem to give up anywhere near the memory they had taken up. When I cut one of these views (there are several in my app) down to just 1-3 images that were 320x460 and left everything else the same, it recaptures the memory just fine.
Is there some issue with using images this large? Am I doing something wrong in this code (pasted below)?
This is a snippet from the viewController that is causing problems.
- (CGFloat)findHeight
{
UIImageView *imageView = nil;
NSArray *subviews = [self.scrollView subviews];
CGFloat maxYLoc = 0;
for (imageView in subviews)
{
if ([imageView isKindOfClass:[UIImageView class]])
{
CGRect frame = imageView.frame;
if ((frame.origin.y + frame.size.height) > maxYLoc) {
maxYLoc = frame.origin.y;
maxYLoc += frame.size.height;
}
}
}
return maxYLoc;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.scrollView setContentSize:CGSizeMake(320, [self findHeight])];
[self.scrollView setCanCancelContentTouches:NO];
self.scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
self.scrollView.clipsToBounds = YES;
self.scrollView.scrollEnabled = YES;
self.scrollView.pagingEnabled = NO;
}
- (void)dealloc {
NSLog(#"DAY Controller Dealloc'd");
self.scrollView = nil;
[super dealloc];
}
UPDATE: I've noticed another weird phenomenon. If I don't use the scroll on the view, it seems to be hanging on to the memory. But if I scroll around a bunch and ensure that all of the UIImageViews became visible at one point, it will free up and regain most of the memory it lost.
UPDATE2: The reason I'm asking this is my app is actually crashing due to low memory. I wouldn't mind if it were just caching and using up extra memory, but it doesn't seem to ever release it - even in didReceiveMmoryWarning conditions
I've solved the mystery - and I'm pretty sure this is a bug on Apple's side.
As Kendall suggested (thanks!), the problem lies in how InterfaceBuilder loads images from the NIB file. When you initFromNib, all UIImageViews will init with a UIImage using the imageNamed: method of UIImage. This call uses caching for the image. Normally, this is what you want. However, with very large images and additionally ones that scroll far off of the visible area, it does not seem to be obeying memory warnings and dumping this cache. This is what I believe to be a bug on Apple's side (please comment if you agree/disagree - I'd like to submit this if others agree). As I said above, the memory used by these images does seem to be released if a user scrolls around enough to make it all visible.
The workaround that I've found (also Kendall's suggestion) is to leave the image name blank in the NIB file. So you lay out your UIImageView elements as normal, but don't select an image. Then in your viewDidLoad code, you go in and load an image using imageWithContentsOfFile: instead. This method does NOT cache the image, and therefore does not cause any memory issues with retaining large images.
Of course, imageNamed: is a lot easier to use, because it defaults to anything in the bundle, rather than having to find the path. However, you can get the path to the bundle with the following:
NSString *fullpath = [[[NSBundle mainBundle] bundlePath];
Putting that all together, here's what that looks like in code:
NSString *fullpath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:[NSString stringWithFormat:#"/%#-%d.png", self.nibName, imageView.tag]];
UIImage *loadImage = [UIImage imageWithContentsOfFile:fullpath];
imageView.image = loadImage;
So adding that to my code above, the full function looks like this:
- (CGFloat)findHeight
{
UIImageView *imageView = nil;
NSArray *subviews = [self.scrollView subviews];
CGFloat maxYLoc = 0;
for (imageView in subviews)
{
if ([imageView isKindOfClass:[UIImageView class]])
{
CGRect frame = imageView.frame;
if ((frame.origin.y + frame.size.height) > maxYLoc) {
maxYLoc = frame.origin.y;
maxYLoc += frame.size.height;
}
NSString *fullpath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:[NSString stringWithFormat:#"/%#-%d.png", self.nibName, imageView.tag]];
NSLog(fullpath);
UIImage *loadImage = [UIImage imageWithContentsOfFile:fullpath];
imageView.image = loadImage;
}
}
return maxYLoc;
}
Separate from your imageNamed caching issues, the answer to your question
I have a large UIScrollView into which
I'm placing 3-4 rather large (320x1500
pixels or so) UIImageView image tiles.
[...] Is there some issue with using
images this large?
Is in the header of the UIImage docs:
You should avoid creating UIImage objects that are greater than 1024 x 1024 in size.
Besides the large amount of memory such an image would consume, you may run into
problems when using the image as a texture in OpenGL ES or when drawing the image
to a view or layer.
Also, Apple claims to have fixed the imageNamed cache-flushing problem in 3.0 and beyond, although I've not tested this extensively, myself.
It could be the system caching references to your images in memory, assuming you have just drug in the images from the media browser in IB the code underneath is probably using the UIImage imageNamed: method...
You could try loading all the images with imageWithContentsOfFile: , or imageWithData: and see if it behaves the same (dragging in unfilled UIImageViews in IB and setting contents in viewDidLoad:).
Read the UIImage class reference if you'd like a little more detail, it also describes which methods are cached.
If it's the cache it's probably OK though as the system would free it if needed (did you also try hitting the Simulate Memory Warning in the simulator?)
Completing the answer of Bdebeez.
One nice idea is to override the imageNamed: calling the imageWithContentsOfFile:.
Here is the idea of the code:
#implementation UIImage(imageNamed_Hack)
+ (UIImage *)imageNamed:(NSString *)name {
return [UIImage imageWithContentsOfFile:[NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] bundlePath], name ] ];
}
#end
Note: With this override you will not have any cache loading UIImages, if you need this, you will have to implement your own cache.
- (void)dealloc {
NSLog(#"DAY Controller Dealloc'd");
[self.scrollView release];
[super dealloc];
}
give that a shot, your #property() definition is requesting it to be retained, but you weren't explicitly releasing the object
From what I learned on its memory management behavior is that views won't get dealloc unless low in memory. Try an official demo like SQLBooks: 1. Run with Leaks Monitor 2. Run through every views it has. 3. Go back to the root view. 4. You will notice the memory usage level is still the same. As Kendall said it may be cached?
I think you shouldn't pay attention on this memory usage pattern -- because when the new images are pointed to the UIScrollView, the old image objects will be released and memory will be freed for new images anyway.
There is a known problem - a memory leak in imageName. I found a really useful solution for it - creating image cash in application delegate, this way optimizing the performance and memory usage in my application.
See this blog post