First off, I am just starting with Xcode and iPhone developing, so please bear with me if these issues are redundant...I haven't found the answer yet, hence my posting. Second...ok, there is second...so I will get to the question.
I am using an UIImage view for the background image of my TheEyeViewController and I need another UIImageView to set on top of the background UIImageView. The foreground UIImageView needs to load a sequence of images...but it's not working. When I try to setup the foreground image view with code, I don't think the compiler is able to differentiate between the 2 image views. It doesn't crash...the sequence just doesn't show up.
Any help on this is greatly appreciated.
Thank you for your time.
//.h
UIImageView *imageView;
#property(nonatomic, retain) IBOutlet UIImageView *imageView;
//.m
- (void)viewDidLoad
{
imageView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"openingSeq1.jpg"],
[UIImage imageNamed:#"openingSeq2.jpg"],
[UIImage imageNamed:#"openingSeq3.jpg"],
[UIImage imageNamed:#"openingSeq4.jpg"],
[UIImage imageNamed:#"openingSeq5.jpg"],
[UIImage imageNamed:#"openingSeq6.jpg"],
[UIImage imageNamed:#"openingSeq7.jpg"],
[UIImage imageNamed:#"openingSeq8.jpg"],
[UIImage imageNamed:#"openingSeq9.jpg"],
[UIImage imageNamed:#"openingSeq10.jpg"],nil];
imageView.animationDuration = 2;
imageView.animationRepeatCount = 1;
[imageView startAnimating];
[self.view addSubview:imageView];
if(imageView.image == nil)
{
labelRandText.text = #"Images didn't load.";
}
[super viewDidLoad];
}
General comments that may or may not help:
Supposing [UIImage imageNamed:#"openingSeq1.jpg"] returns nil, that's the same as posting an empty array because that nil will look like the one that ends the list of things passed to arrayWithObjects:. It's probably worth adding a quick:
NSLog(#"os1: %#", [UIImage imageNamed:#"openingSeq1.jpg"]);
If that shows that you're getting nil back then your project is set up incorrectly, such that UIImage can't find the files.
If imageView is already in your view in Interface Builder then [self.view addSubview:imageView]; is redundant, but shouldn't be harmful.
An NSLog(#"%#", imageView) (or even one that logs both the imageView you're trying to reach and the other one that you think may be problematic, so you can check they're not the same one) can be used to verify that you have things wired up correctly in Interface Builder.
Also, technically you should call [super viewDidLoad]; before any of your own code, because logically you want the superclass to have done whatever it should do before you do whatever you should do. However, as with addSubview, this shouldn't really make any odds in your particular case because the UIViewController base class doesn't do anything in viewDidLoad.
Related
Hi my program adds small Images to main view. I have this undo button to remove recently added Image(subView). It works ok when it has all different Images, But when there are two same images it occurs error.
I think this is because it both points the same original png file. But I have no idea how to fix it. Please give me some hint.
add{
UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:#"pah%d",tagNum]];
TouchImageView *touchImageView = [[TouchImageView alloc] initWithFrame:imageRect];
imageCounter++;
touchImageView.tag = imageCounter;
touchImageView.image = image;
touchImageView.center = CGPointMake(160.0, 230.0);
[view addSubview:touchImageView];
}
undo{
[[self.view viewWithTag:imageCounter] removeFromSuperview];
imageCounter--;
}
I doubt that its your problem here but imageNamed: caches the image in memory with an internal caching system. Every time you ask for [UIImage imageNamed:#"foo"] you get the same UIImage instance.
You probably want to be using imageWithContentsOfFile: instead which returns a unique instance of a UIImage.
Try that and see if it makes a difference.
if you just have to remove recently added image...
then each time where you are adding the image store its reference like this - it will work well with ARC...
UIImageView *imageView = touchImageView;
then in your remove Recently added image button click's
for(UIImageView *iV in view.subviews)
{
if(iV == imageView)
{
[iV removeFromSuperView];
}
}
i think it will work ...
In my application,i created an imageview on the above the tabbar.I want to display some images here for displaying some adds there.
What my actual problem is...i added this on my table view and whenever i am scrolling the table view ,my imageview is also scrolling.Please help me in this
Thanks in advance.Here is my code
UIImageView *currentLocationImageView = [[UIImageView alloc] init];
NSURL *url1 = [NSURL URLWithString:#"http://imgsrv.995themountain.com/image/kqmt2/UserFiles/Image/SiteGraphics/MTNVideos360x80.jpg"];
UIImage *img1 = [UIImage imageWithData: [NSData dataWithContentsOfURL:url1]];
NSURL *url2 = [NSURL URLWithString:#"http://download.xbox.com/content/images/35f6c527-fb73-40d3-bcb9-bdea2680bc03/1033/banner.png"];
UIImage *img2 = [UIImage imageWithData: [NSData dataWithContentsOfURL:url2]];
NSArray *images = [NSArray arrayWithObjects:img1, img2, nil];
[currentLocationImageView setAnimationImages:images];
[currentLocationImageView setAnimationRepeatCount:0];
[currentLocationImageView setAnimationDuration:5.0];
currentLocationImageView.frame = CGRectMake(0.0, 340.0, 320.0, 30.0);
//self.tableView.tableFooterView = currentLocationImageView;
[currentLocationImageView startAnimating];
[self.view addSubview:currentLocationImageView];
Without seeing more code, it looks as though you may be using a UITableViewController instead of a UIViewController. The difference between the two is important in the case of your last line [self.view addSubview:currentLocationImageView]. What that does in a UIViewController is adds it to the view that would be containing the tableview and the imageview. However, in a UITableViewController the self.view property holds the tableview itself, therefor, it ads your image view as a subview of the tableview, subjecting it to the tableview's scrolling behaviour.
What you can do is change from using a UITableViewController (probably going to be trivial for your application, but may be less than trivial depending on why you opted to use it in the first place); and you'll also need to explicitly create the tableview, and add it to the backing view of the UIViewController subclass you're writing—akin to how you're adding the imageview above.
Hope this helps.
i'm changing image of UIImageview by [self setImage: newImage];
Looks like every time I does that with newImage, prior image doesn't seem to be released.
What's the correct way to replace image of UIImageView?
Thank you
Yes, UIImageView setImage does leak!
Actually, leaks CGImage, not UIImage (as instrument "allocation" shows)
I use BrutalUIImage instead of UIImage
#interface BrutalUIImageView : UIView {
UIImage *image;
}
#property(nonatomic, retain) UIImage *image;
#end
#implementation BrutalUIImageView
#synthesize image;
- (void)setImage:(UIImage *)anImage {
[image autorelease];
image = [anImage retain];
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
[image drawInRect:rect];
}
- (void)dealloc {
[image release];
[super dealloc];
}
#end
UIImageView setImage: never leaks, unless the image you are passing doesn't get released.
Your code wont leak, if your are assigning an autoreleased image to the image view, something like the following.
UIImage *newImage = [UIImage imageNamed:#"sampleImage"];
[yourImageView setImage:newImage];
But, if you are allocating the image somewhere, you have to release it manually.
Your BrutalUIImageVIew class is really interesting, but by drawing the Image using UIImage "drawInRect:" method, i loss the transparent areas of my PNG file.
Do you know how to draw the image, keeping the PNG transparence ?
(Of course, not using UIImageVIew wich leaks the CGImage while calling "setImage:")
Yes UIImageView setImage indeed leaks!
If you cycle through a bunch of images with
[yourImageView setImage:[UIImage imageNamed:#"sampleImage.png"]];
you can see on instruments memory usage increasing.
This seems to be some kind of caching going around since after
cycling through all the images memory usage will go flat.
The correct, or at least, the non leaky way to do it is:
NSString *thePath = [[NSBundle mainBundle] pathForResource:#"sampleImage" ofType:#"png"];
UIImage *newImage = [[UIImage alloc] initWithContentsOfFile:thePath];
[yourImageView setImage:newImage];
I verified this on my code as my APP was cycling through a lot
of large image files.
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.
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