Objective-C: Image Management between classes - objective-c

I am trying to create a model with multiple images in a factory model style. I have one class that handles the individuals, one class that handles the group of individuals and a ViewController.
testImage = [[UIImageView alloc] initWithFrame:CGRectMake(250, 500, 30, 30)];
testImage.image = [UIImage imageNamed:#"myImage.png"];
testImage.animationDuration = 1.5;
testImage.contentMode = UIViewContentModeCenter;
[self.view addSubview:testImage];
http://jcdeveloperworld.blogspot.com/2009/07/iphone-game-tutorial-part-1.html -- Thanks
That code works when it is just in the viewController, and now I am trying to make the individuals keep track of their own image. So i put the
testImage = [[UIImageView alloc] initWithFrame:CGRectMake(250, 500, 30, 30)];
testImage.image = [UIImage imageNamed:#"myImage.png"];
testImage.animationDuration = 1.5;
testImage.contentMode = UIViewContentModeCenter;
[self.view addSubview:testImage];
in a separate class and then tried to display it from my viewController. It is my understanding that I can't just tell the viewController to display something from a separate class.
flock *newFlock = [[flock alloc] init];
[newFlock makeFlock];
NSMutableArray *tempFlock = [[NSMutableArray alloc] init];
tempFlock = newFlock.theFlock;
individualsClass *tempIndividual = [[individualsClass alloc] init];
tempIndividual = [tempFlock objectAtIndex:0];
UIImageView *tempImage = [[UIImageView alloc] initWithFrame:CGRectMake(250, 500, 30, 30)];
tempImage = tempIndividual.individualImage;
[self.view addSubview:tempImage];
I also tried it without all the copying, but the dot notation started to get in the way because I'm accessing properties of objects in an NSMutableArray (which seems absurdly difficult in objective C). I could use some help in figuring that out too
ie.
flock.[theFlock.individualImage.image objectAtIndex:1] //Doesn't work
I am kind of new to objective C, so perhaps I'm attacking this problem wrong. This is being done for an iPad app

It's not clear what you're trying to do, but you're definitely doing a lot of stuff you don't need to. Consider these lines of your code:
NSMutableArray *tempFlock = [[NSMutableArray alloc] init];
tempFlock = newFlock.theFlock;
The first line declares a variable named tempFlock of type NSMutableArray *. Then it creates and initializes an empty NSMutableArray and assigns it to tempFlock. The second line then assigns newFlock.theFlock to tempFlock, overwriting the only reference to the array you created in the first line. You can just say this:
NSMutableArray *tempFlock = newFlock.theFlock;
Anyway, the correct syntax for your last block is this:
[flock.theFlock objectAtIndex:1].individualImage.image
So maybe this is what you want to do:
[self.view addSubview:[flock.theFlock objectAtIndex:1].individualImage];
Also, flock and individualsClass are not good class names in Objective-C. Flock and Individual might be better names.
And it's confusing that flock instances have a property named theFlock. It looks like theFlock is just an array of individuals, so a better name for the property would probably be individuals.

If all the "dot" syntax is causing problems with understanding just don't use them, they are just an alternate syntax for method invocation.
[myInstance value] is the same as myInstance.value, which can be viewed as a simple substitution.
Also avoiding long compound statements can help understanding, instead use one or more intermediate well named variables. Trust the compiler to optimize them.
So, as #rob writes:
[flock.theFlock objectAtIndex:1].individualImage.image
the equivalent could be:
[[[[flock theFlock] objectAtIndex:1] individualImage] image]
Broken into multiple statements:
tempFlock = [[flock theFlock] objectAtIndex:1];
tempIndividual = [tempFlock individualImage];
image = [tempIndividual image];

Related

Creating a UIImageView using an NSString

I have to create a number of UIImageViews based on values stored in an NSString. The value that is stored will become the name for my UIImageView, each one will be created individually of course.
So to keep things simple my question is, can one create a UIImageView based on the value stored within an NSString?
Rough Example:
-(UIImageView)createMyFruitViews:(NSString *)fruit
{
NSString *fruit = #"";
UIImageView *fruitImage [fruit value]; //<-- made up for example purposes.
return fruitImage;
}
If this is not possible, could you please suggest a way around this?
FYI I've read through the forums too, the only thing that comes close is How to use NSSstring to instantiate a UIImageview; however I don't understand the answer and I am not sure that it can apply to what I need.
Try using UIImageView *fruitImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:fruit]];
I'm not quite sure I understood what you need... as far as I can tell, you have the name of an image on a NSString object, right?
Lets say you have an array of strings, each string representing the name of an image:
NSArray *myImageNames = [NSArray arrayWithObjects:#"image1.png", #"image2.png", #"image3.png", nil];
Then you'll create your imageViews using these images:
for (NSString *imageName in myImageNames) {
UIImageView *myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]];
[self.view addSubview:myImageView];
[myImageView release]; // If you're not using ARC
}
If I didn't get exactly what you need, please let me know.

Is there a way to set the same image to many UIimageViews?

I'm working with an image heavy iOS app and I found myself typing almost the same line again and again:
...
A016.image = [UIImage imageNamed:img21];
A017.image = [UIImage imageNamed:img21];
A018.image = [UIImage imageNamed:img21];
...
Now I ask you: is there a way where I could store the UIImageViews names in an Array or something?
Only to beautify my very ugly code.
/John
Yes, you can put the UIImageViews in an array and put it in a for.
UIImageView * imageView1;
UIImageView * imageView2;
UIImageView * imageView3;
NSArray * imageViewsArray = [NSArray arrayWithObjects:imageView1,imageView2,imageView3,nil];
for (UIImageView * currentImageView in imageViewsArray) {
currentImageView.image = [UIImage imageNamed:img21];
}
Of course you can simplify something like that. But it's not entirely clear what the context is exactly.
If you have all your image views in an array you could do something like this.
// Assume an NSArray called imageViews exists with all the UIImageView instances in it.
for (UIImageView *imageView in imageViews) {
imageView.image = [UIImage imageNamed:img21];
}
If your image views aren't in an array already and are actually called A016, A017 etc., then I'd recommend you change your code design. Something like this is never a good idea. It results in bad, difficult to maintain code. If this is some kind of table of images, try putting the image views in an array in the first place.
That being said, there are ways to put variables like that in an array. If they are declared as #properties or ivars you can could do something like this.
NSMutableArray *imageViews = [NSMutableArray arrayWithCapacity:20];
for (int i = 0; i < 20; i++) {
NSString *variableName = [NSString stringWithFormat:#"A0%d", i];
UIImageView *imageView = [self valueForKey:variableName];
[imageViews addObject:imageView];
}
After that you can save the array as an ivar and do the same thing as above.
A more hardcoded version would be to simply put the image views into an array yourself:
NSArray *imageViews = [NSArray arrayWithObjects:..., A016, A017, A018,..., nil];
But I really recommend not using indexed variable names like that.

creating an object and setting a retaining property

How should I set a retained property when creating an object using alloc and init? (Without using autorelease)
With this line in the header (and the corresponding #synthesize line in the implementation):
#property(retain)UIWebView *webView;
These are the three options I have (I think):
UIWebView *tempWebView = [[UIWebView alloc] init];
[tempWebView setDelegate:self];
tempWebView.hidden = YES;
self.webView = tempWebView;
[tempWebView release];
(This one seems the best concerning memory management but it's more lines of code and involves a stupid variable name, so a decrease in readability)
self.webView = [[UIWebView alloc] init];
[self.webView release];
[self.webView setDelegate:self];
self.webView.hidden = YES;
(This one it's more obvious whats happening but the memory management doesn't seem that great, also Xcode's Analyser doesn't like it)
webView = [[UIWebView alloc] init];
[self.webView setDelegate:self];
self.webView.hidden = YES;
(This one is the shortest, it's more obvious than the first example whats happening. But it bypasses the setter, so should a custom implementation of the setter be implemented later it won't work in this case)
So which example should be used, or is there a better way?
The best option, IMO, is the one you don't like, i.e. using autorelease:
self.webView = [[[UIWebView alloc] init] autorelease];
If you do not want to and want a one-liner initialization, the only option is your third one:
webView = [[UIWebView alloc] init];
since all the others requires an explicit line to do an extra release.
I don't see it as bad, especially when it belongs to the init method and you don't reassign it elsewhere without using the property, and I myself use it when it seems reasonable to me.
What works really well with retained properties are convenience constructors like:
self.image = [UIImage imageWithContentsOfFile:xxxxxxx];
So, possibly if you really find that none of the options you listed is fine with you, you might add a category to UIWebView and a convenience constructor doing the autorelease job for you:
self.webView = [UIWebView webViewWith......];

Create multiple UITextFields programmatically using a for loop

Brand new to coding; been utilizing stackoverflow religiously for months; first time asking a question; please be gentle.
I'm trying to create a series of UITextFields programmatically using a for loop. The fields should have names like "pax1name", "pax2name", "pax3name", etc.
The piece I'm missing is how to take a string and make it the name of a textField, changing the name of the textField each time the for-loop executes.
Here's my code, but maybe I'm going about this the wrong way? Thank you in advance!
// set up the names textfields
for (int i = 0; i < 7; i++) {
NSString *fieldName = [[NSString alloc] initWithFormat:#"pax%iname", (i + 1)];
// I can't figure out what goes here to create a UITextField with the name of fieldName
textField = [[UITextField alloc] initWithFrame:CGRectMake(15, (15 + (i * 40)), 400, 40)];
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.font = [UIFont systemFontOfSize:15.0];
textField.autocorrectionType = UITextAutocorrectionTypeNo;
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
[namesViewController addSubview: textField];
[fieldName release];
[textField release];
}
Normally you use the UIView property tag for that. Use [textField setTag:<youCustomTag>] in your loop to set the value, e.g. your variable i. Please note that tag expect an NSUInteger and not an NSString.
To access the correct text field afterwards you'd call [[namesViewController view] viewWithTag:<yourCustomTag>].
BUT, is it really necessary to create multiple text fields for your view controller? There might be a more elegant solution by creating just one single text field and setting the tag-property on demand when the user taps a row. I don't know if that would work for you.
I think you've been going the wrong way about this. UITextField has no property called "name". To identify a control, you can use its tag property. See this code:
for (int i = 0; i < 7; i++) {
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(...)];
textField.tag = i + 1;
...
}
When you say the name of the textfield I'm presuming you mean the variable name rather than setting the text that is displayed. I.e. you want to create the variables in a loop, but later be able to reference them individually by name.
In that case, you can't do what you want to do. What you can do is stick them in an array for handy access later on.
NSMutableArray *paxNameFields = [[NSMutableArray alloc] init]; // before your loop
[paxNameFields addObject: textField]; // inside the loop
[paxNameFields objectAtIndex: 5]; //sometime later use the 6th field
Or if you just want to be able to identify which UITextField you're passed into a delegate callback later on, you can set/check the tag property.
UITextFields don't HAVE names. You're probably coming from HTML-world, where these things are basically a big hash of name-value pairs. Not like that in Cocoa Touch. As #Florian Mielke says, they each have an int .tag property, which is probably what you want to set.

Proper Memory Management for Objective-C Method

I'm programming an iPhone app and I had a question about memory management in one of my methods. I'm still a little new to managing memory manually, so I'm sorry if this question seems elementary.
Below is a method designed to allow a number pad to place buttons in a label based on their tag, this way I don't need to make a method for each button. The method works fine, I'm just wondering if I'm responsible for releasing any of the variables I make in the function.
The application crashes if I try to release any of the variables, so I'm a little confused about my responsibility regarding memory.
Here's the method:
FYI the variable firstValue is my label, it's the only variable not declared in the method.
-(IBAction)inputNumbersFromButtons:(id)sender {
UIButton *placeHolderButton = [[UIButton alloc] init];
placeHolderButton = sender;
NSString *placeHolderString = [[NSString alloc] init];
placeHolderString = [placeHolderString stringByAppendingString:firstValue.text];
NSString *addThisNumber = [[NSString alloc] init];
int i = placeHolderButton.tag;
addThisNumber = [NSString stringWithFormat:#"%i", i];
NSString *newLabelText = [[NSString alloc] init];
newLabelText = [placeHolderString stringByAppendingString:addThisNumber];
[firstValue setText:newLabelText];
//[placeHolderButton release];
//[placeHolderString release];
//[addThisNumber release];
//[newLabelText release];
}
The application works fine with those last four lines commented out, but it seems to me like I should be releasing these variables here. If I'm wrong about that I'd welcome a quick explanation about when it's necessary to release variables declared in functions and when it's not. Thanks.
Yes, you need to release them, but you need them just a little longer than beyond the end of your function.
The solution is called autorelease. Just replace release with autorelease and the objects stay around until the program gets back to the runloop.
When the program gets back there, everybody interested in one of the objects should have sent a retain message to it, so the object will not be deallocated when released by the NSAutoreleasePool.
edit actually, looking at your code, there's a lot more wrong with it. E.g. this:
UIButton *placeHolderButton = [[UIButton alloc] init];
placeHolderButton = sender;
doesn't make sense. First you allocate an object, then assign (a pointer to) it to variable placeHolderButton. That's fine.
Then you assign sender to that same variable. The reference to the object you just created is now lost.
Not sure if I get what you want, but this would be better:
-(IBAction)inputNumbersFromButtons:(id)sender {
UIButton *placeHolderButton = sender; // this is still a little useless, but ok
int i = placeHolderButton.tag;
NSString *addThisNumber = [NSString stringWithFormat:#"%i", i];
NSString *placeHolderString = firstValue.text;
NSString *newLabelText = [placeHolderString stringByAppendingString:addThisNumber];
[firstValue setText:newLabelText];
}
No allocs, so no releases necessary. The strings returned by those functions are already added to the autoreleasepool, so they will be deallocated automatically (if needed).
Well. Release them when you are done with them. The sooner the better. Some objects are tricky if you are new to memory management.
Release them in the dealloc method then.
The auto release pool can be handy, some people might disagree according to the performance issues.
you need to release anything containing the word new, alloc/init or copy.
also, you don't need to alloc/init this:
UIButton *placeHolderButton = [[UIButton alloc] init];
placeHolderButton = sender;
another way of doing this is:
UIButton *placeHolderButton = (UIButton *)sender;
in your version, it is allocating an instance with a retain count of +1, but you are immediately replacing the reference, so there is no way of releasing the memory later.
you are creating a lot of instances with alloc/init, and then replacing their references with autoreleased instances.
you could use
NSString *placeHolderString = [placeHolderString stringByAppendingString:firstValue.text];
instead of
NSString *placeHolderString = [[NSString alloc] init];
placeHolderString = [placeHolderString stringByAppendingString:firstValue.text];
which is again replacing a manually managed instance created on the first line, with an autoreleased instance on the second.
infact you could replace every alloc/init in this with the factory method and not have to deal with memory at all in it as they would be autoreleased instances.
-(IBAction)inputNumbersFromButtons:(id)sender {
//cast sender as a UIButton to suppress compiler warning, and allow us to reference it as placeholder button
UIButton *placeHolderButton = (UIButton *) sender;
int i = placeHolderButton.tag;
NSString *addThisNumber = [NSString stringWithFormat:#"%i", i];
[firstValue setText:[firstValue.text stringByAppendingString:addThisNumber]];
}
If you look at the class docs for NSString, any method with a + next to it(ie +stringWithString:(NSString *)string) is a class method, don't use these methods on a reference after you have called alloc/init on it.
I find it puzzling that you use alloc/init on a UIButton.
I always use the factory methods, e.g.
UIButton* aButton = [UIButton buttonWithType:UIButtonTypeCustom];
This returns an autoreleased button which I immediately add to its intended parent view.
Can't confirm it right now, but it looks as if the SDK caches UIButton instances and performs some optimizations behind the scenes. Every time I tried to retain a UIButton ivar, performance has degraded (especially when there is many sub views on screen)