setImage: does not cause image to change - objective-c

I have created two UIImageViews in my code:
UIImageView *background = [[UIImageView alloc]initWithFrame:CGRectMake(150, 60, 180, 180)];
UIImageView *result = [[UIImageView alloc]initWithFrame:CGRectMake(150, 60, 180, 180)];
[self.view addSubview:background];
[self.view addSubview:result];
[self.view insertSubview:result aboveSubview:background];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[background setImage:[UIImage imageNamed:[defaults objectForKey:#"colour"]]];
When you press a button, this code is executed:
- (IBAction)oneToSix {
int rNumber = (arc4random() % 6) + 1;
[result setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%d", rNumber, #".png"]]];
}
but the image doesn't change.
Also, I am getting these errors:

You are redeclaring variables in the method scope that were already defined at the class scope. Remove the UIImageView * part from your initialization.
Note: it's doing nothing when you change image, because in ObjectiveC, a method call to nil (uninitialized instance member) is valid, and produces no visible result.

[result setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%d", rNumber, #".png"]]];
Note you are only putting the number into that string. You'll want %d%# as the format.
Those aren't errors, they are warnings. You have an instance variable in your class called background. You also declare a local variable called background in your method. The warning is to let you know that there's a conflict and that it's choosing the local variable over the instance variable.

Is the image named Red or Red.png? Because if it is the latter, you should append the file extension in the setImage method with something like [NSString stringWithFormat:#"%#.png", [defaults objectForKey:"colour"]]
Also, you are inserting the result imageView twice. Remove the first one.

Related

Memory leak not detected by static analyzer

I came across this piece of code
UIImageView *image = [[UIImageView alloc] initWithFrame:imageFrame];
[image setImage:[UIImage imageNamed:#"myImage"]];
[self.view addSubview:image];
image = nil;
Given that ARC is not used, I assume that it will lead to a memory leak on the image object.
Nevertheless the static analyzer doesn't catch that.
I'm wondering who is mistaken, the static analyzer or me, and I'd like a second opinion on that.
Does the above code actually leak?
It turned out the one mistaken was me (duh!)
The application I'm auditing is pretty large, and I missed the fact that the developers enabled ARC with the -fobjc-arc flag on specific classes, including the one the above snippet is taken from.
Long live to the static analyzer!
self.view has an array of pointers to its subviews that will keep your image accessible and keep the reference counter (used for ARC) above 0.
CGRect imageFrame = CGRectMake(100, 100, 100, 200);
UIImageView *image = [[UIImageView alloc] initWithFrame:imageFrame];
NSLog(#"image=%#", image);
[image setImage:[UIImage imageNamed:#"Default"]];
[self.view addSubview:image];
image = nil;
UIView *v = [[self.view subviews] objectAtIndex:0];
NSLog(#"v=%#", v);
When you put self.view = nil; the reference counter for image will drop and the memory will be free'd. I don't think you can create a leak this way :)

EXC_BAD_ACCESS fatal error

I have got this code which creates an image:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
background = [[UIImageView alloc]initWithFrame:CGRectMake(150, 60, 180, 180)];
result = [[UIImageView alloc]initWithFrame:CGRectMake(150, 60, 180, 180)];
[background setImage:[UIImage imageNamed:[defaults objectForKey:#"colour"]]];
[self.view addSubview:background];
[self.view insertSubview:result aboveSubview:background];
This is in my viewWillAppear. When I press a button, this happens:
- (IBAction)oneToSix {
int rNumber = (arc4random() % 6) + 1;
[result setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%#", rNumber]]];
}
But it gets this fatal error: Thread one: Program received signal: "EXC_BAD_ACCESS". on the setImage part when I press the button. What is the problem? I am new to Objective C.
I think the problem is string format in NSString, you should change the code to :
[result setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%d", rNumber]]];
as rNumber is an int.
And you should release the background and result, as viewWillAppear will tend to be called every time the view is appear so will result a memory leak.

Cocoa Touch: Adding images in for loop

this is crashing on me when I put in an array of image urls, I think its because of me creating the UIImageView and then trying to do it again with the same name!
- (void)cycleImages:(NSArray *)images {
int fromLeft = 5;
for(int n = 0; n <= [images count]; n++){
UIImageView *myImageView = [[UIImageView alloc] initWithImage:[[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[images objectAtIndex:n]]]]];
myImageView.frame = CGRectMake(fromLeft, 5, 48, 48); // from left, from top, height, width
fromLeft = fromLeft + 53;
//add image view to view
[self.view addSubview:myImageView];
NSLog(#"%#", [images objectAtIndex:n]);
}
}
Any ideas?
Thanks!
Dex
First, you should use fast enumeration instead of an index loop. Fast enumeration is simpler, clearer, and faster—whenever you don't specifically need an index for something, there is no reason not to use fast enumeration.
UIImageView *myImageView = [[UIImageView alloc] initWithImage:[[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[images objectAtIndex:n]]]]];
This assumes that [images objectAtIndex:n] is a string. Is that what you put into the array?
Also, because you created that image view with alloc, you need to release it. If you're using ARC, that's done automatically, so you don't need to do anything; otherwise, you need to either send it release after adding it as a subview or send it autorelease.
this is crashing on me …
Please be more specific. What sort of “crash” do you get, and on what line?
If you get a crash log, please edit your question to include it.

problem with UiImageView.image = nil

.h file
#interface AppViewController : UIViewController {
...
UIImageView *imageViewArray[3][5];// not using any #property for this
...
}
...
#define FOR(j,q) for (int j = 0; j < q; j++)
.m file
ViewDidLoad{
FOR(i,5){
FOR(j,3)
{
image_names[0] = [UIImage imageNamed:[NSString stringWithFormat:#"%d.png",j]];
imageViewArray[j][i] = [[UIImageView alloc] initWithImage: image_names[0]];
CGRect newFrame ;
newFrame = CGRectMake(60+(j*130), 110+(150*i),130,136);
imageViewArray[j][i].frame = newFrame;
[self.view addSubview:imageViewArray[j][i]];
}
}
}
//clear the previous images and call a method to add new
-(IBAction)Change{
FOR(i,5)
FOR(p,3)
imageViewArray[p][i].image = nil;
[self ImageSwitch];
}
-(void)ImageSwitch{
FOR(i,5){
FOR(j,3)
{
image_namesTmp[0] = [UIImage imageNamed:[NSString stringWithFormat:#"%d.png",j+3]];
imageViewArray[j][i] = [[UIImageView alloc] initWithImage: image_namesTmp[0]];
CGRect newFrame ;
newFrame = CGRectMake(60+(j*130), 110+(150*i),130,136);
imageViewArray[j][i].frame = newFrame;
[self.view addSubview:imageViewArray[j][i]];
}
}
}
when i press the button for the first time it works fine(old images get replaced with new ones),but if i do it second time
imageViewArray[p][i].image = nil;
this line wont work and all the previous images still remain there and new images are overlapping with the present ones
In the ImageSwitch method you are always creating new instances of UIImageView.
imageViewArray[j][i] = [[UIImageView alloc] initWithImage: image_namesTmp[0]];
At this point, there already is a reference to UIImageView stored in the variable. You are losing your reference to it and since you alloc/inited it, you will most probably also leak its memory. Moreover, you are adding new UIImageViews to the view hierarchy, but you are not removing the old ones.
Try something like this:
[imageViewArray[j][i] removeFromSuperview];
[imageViewArray[j][i] release];
imageViewArray[j][i] = [[UIImageView alloc] initWithImage: image_namesTmp[0]];
Second time you set your images you don't need to recreate the UIImageView objects. Your ImageSwitch method is better declared as,
-(void)ImageSwitch{
FOR(i,5){
FOR(j,3)
{
imageViewArray[j][i].image = [UIImage imageNamed:[NSString stringWithFormat:#"%d.png",j+3]];
}
}
}
Just changing the images will suffice and that's what you need to do.
You need an NSMutableArray of ImageViews. In your header declare the NSMutableArray:
NSMutableArray *_imageViewArray;
Then in your viewDidLoad method initialize the array and then populate it with your image views.
_imageViewArray = [[NSMutableArray alloc]init];
for (int i =0; i < imageNames; i++) {
UIImageView *tempImageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:[imageNames objectAtIndex:i];
[_imageViewArray addObject:tempImageView];
[tempImageView release];
}
So thats the image view array populated, you may do it a different way but thats up to you.
To switch the views over what I would do is like so...
[self.view removeAllSubviews]; // gets rid of all previous subviews.
[self.view addSubview:[_imageViewArray objectAtIndex:3]];// could be any number. generate a random number if you want.
Thats all you would need to do. If you wanted to know which image view was being displauyed the maybe declare an instance of UIImageView in your header called _currentImageView and then you can remove just that image view instead of all subviews.
Hope this helped.

Creating a method for wrapping a loaded image in a UIImageView

I have the following un my applicationDidFinishLaunching method
UIImage *image2 = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"image2.jpg" ofType:nil]];
view2 = [[UIImageView alloc] initWithImage:image2];
view2.hidden = YES;
[containerView addSubview:view2];
I'm simply adding an image to a view. But because I need to add 30-40 images I need to wrap the above in a function (which returns a UIImageView) and then call it from a loop.
This is my first attempt at creating the function
-(UIImageView)wrapImage:(NSString *)imagePath
{
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:imagePath
ofType:nil]];
UIImageView *view = [[UIImageView alloc] initWithImage:image];
view.hidden = YES;
return view;
}
And then to invoke it I have the following so far, for simplicity I am only wrapping 3 images
//Create an array and add elements to it
NSMutableArray *anArray = [[NSMutableArray alloc] init];
[anArray addObject:#"image1.jpg"];
[anArray addObject:#"image2.jpg"];
[anArray addObject:#"image3.jpg"];
//Use a for each loop to iterate through the array
for (NSString *s in anArray) {
UIImageView *wrappedImgInView=[self wrapImage:s];
[containerView addSubview:wrappedImgInView];
NSLog(s);
}
//Release the array
[anArray release];
I have 2 general questions
Is my approach correct?, ie following best practices, for what I am trying to do (load a multiple number of images(jpgs, pngs, etc.) and adding them to a container view)
For this to work properly with a large number of images, do I need to keep my array creation separate from my method invocation?
Any other suggestions welcome!
Just a note, in function declaration you should return pointer to UIImageView, not an UIImageView itself (i.e. add an asterisk).
Also, when returning the view from the function you should autorelease it. Otherwise it will leak memory. So initialization should look something like this:
UIImageView *view = [[[UIImageView alloc] initWithImage:image] autorelease];
Everything else seems to look good.