I'm trying to add multiple instances of UIImageView(s) to an NSMutableArray of NSMutableDictionar(ies).
I am doing this, to be able to go back and pull out the UIImageView reference, to change the position of these images.
It seems that, when I pull the info out of the Dictionary, that it only returns a null pointer...
Any suggestions on how I should implement this? There may be a way easier method to store a list of n images and be able to reference them later....
// (for i = 0 to 30)
UIImageView *newBall = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"ball.png"]];
newBall.center = CGPointMake (arc4random() % 280 + 20, arc4random() % 440 + 20);
[self.view addSubview:newBall];
NSMutableDictionary *dictBall = [[[NSMutableDictionary alloc] init] autorelease];
[dictBall setValue:newBall forKey:#"imgBall"];
// the dictionary is then added to the array
[newBall release];
// end for loop
(I'm using a Dictionary, because I also store a dy/dx value for rate of change in movement)
Code above doesn't look OK to me.
You're creating a new NSMutableDictionary *dictBall everytime through the loop. Is something retaining it after the loop exists? Doesn't look like it from your code. This may be why your object is nil.
Move this line outside/before the for loop, and you should be fine.
Also, are you sure you want to store UIViewImage in a dictionary? Why not just an ID (based on the tag) and the coordinates (CGPoint or CGRect)? This way you can use the tag to find the UIimageView via the tag and move it where ever you want? Less expensive memory wise.
Related
I am producing a JSON string that I need to parse out and display onto the page. My JSON string outputs information about the contents of a CD like this:
[{"song_name":"Song 1","artist":"John","price":"$1"},
{"song_name":"Song 2","artist":"Anna","price":"$2"},
{"song_name":"Song 3","artist":"Ryan","price":"$3"}]
I would like to display the contents in my viewController in a list format displaying the song_name, artist, and price. I do not want to use a tableView to display this information, but would rather just have a list displayed. How might I go about doing this? I would assume that I need to use NSJSONSerialization, but have only used that for a tableView in the past. Thank you!
In addition to other answers, you can use only one label, just create NSMutableString (for dynamicly adding tracks info) with #"\n" between tracks info, pass it to label.text and set UILabel's property numberOfLines to 0
Follow these steps:
Parse the JSON and store the key-value pair(NSDictionary of CDs) in an NSArray (say infoArray)
Add a UIScrollview as a subview on your viewController's view.
Allocate UILabels dynamically, depending on infoArray count. Looking at your data I believe you can initialize labels with static frames i.e your y can remain static.
Add the text from the infoArray on this label.
Still, I would suggest use UITableView only. It is much simpler and a better approach
You make an array of dictionaries using NSJSONSerialization indeed, then you parse this array one by one and create a view of every dictionary. You're probably best of using a method for this:
-(UIView *) listView: (NSString *)songName andArtist:(NSString *)artist andPrice:(NSString *)price andIndex:(int)viewIndex {
//create your view here and do anything you want
UIView *subView = [[UIView alloc] init] autoRelease];
subView.frame = CGRectMake(0, viewIndex * 70, self.view.frame.width, 70);
//add labels and other stuff
// return the view
return subView;
}
The you add it to the current view by setting different Y values so they appear underneath each other by using the viewIndex of the former method... So if you have an array it goes something like this:
for (int i = 0; i < [array count]; i++) {
NSDictionary *dict = [array objectAtIndex:i];
NSString *songName = [dict valueForKey:#"song_name"];
NSString *artist = [dict valueForKey:#"artist"];
NSString *price = [dict valueForKey:#"price"];
UIView *tempView = [self listView:songName andArtist:artist andPrice:price andIndex:i];
[self.view addSubView:tempView];
}
You have to add it all to a scrollview otherwise you will run into the problem of to many rows on the page. Google for UIScrollView if you don't know how.
But I would recommend against this approach.. Tableviews are there with a reason. They are made for this stuff. Because the also provide for scrolling, drawing and refreshing. If you can, use them!
What's the correct way of creating an UILabel and releasing it again when I want to re-use this until i'm out of records in my array?
I wanted to this this:
// create label
UILabel *labelIWishToRecycle;
for (int i = 0; i < [myArrayFullOfItems count]; i++) {
// Edit the label
labelIWishToRecycle = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 640)];
}
// Release label
[labelIWishToRecycle release];
When I do it this way I get a crash for releasing it. So I just don't release it. Works like a charm now. Of course, this is not the way, so I was wondering what IS.
Should I create and release the label for every item in the array? Or should I create it outside the for-loop but release it within? Or ...?
Thanks in advance.
For what are you using the label!? If you are adding it to a view, than you have to use a new instance everytime.. if you only do size calculations or stuff like that, than you could use the same label over and over again.
But the trick is to initialize your local variable:
UILabel *labelIWishToRecycle = nil;
If you have done this, it is save to send release on it (also if no real label has been assigned yet). Before, your pointer is pointing on a random address and you are trying to release the object at that address. That will be a crash in the most cases.
(Guessing the problem case is the one, when your array count is zero.)
Am I trying to create a game where an obstacle is constantly fired at the user. I have an NSMutableArray so I can access all the obstacles as a group. Here is my code:
CALayer *obstacle = [[[CALayer alloc] init] autorelease];
UIImage *obstacleImage = [UIImage imageNamed:#"Obstacle.png"];
obstacle.contents = (id)obstacleImage.CGImage;
obstacle.bounds = CGRectMake(0, 0, starImage.size.width/2, starImage.size.width/2);
int xPosition = (arc4random()%(360-0))+0;
obstacle.position = CGPointMake(xPosition, 20);
[self.view.layer addSublayer:obstacle];
[self.obstacleArray addObject:obstacle];
My questions is: How would I access the objects in this array? I want to be able to access the latest object so I can animate it. I have looked through the NSMutableArray Class Reference , but still can't find anything. I have tried this:
NSLog(#"%d",[obstacleArray indexOfObject:obstacle]);
But all it returns is: 0. Is there an easy solution to this problem that I'm just not seeing? Thanks in advance for any responses.
Use [obstacleArray lastObject] or [obstacleArray objectAtIndex:[obstacleArray count]-1] to get the last Object. You can find that in the NSArray Class Reference. (Since it's the parent class of NSMutableArray)
I am new to Obj C, but familiar with OOP. I am working on a game that uses collision detection to score points and change UIImageView properties like background color.
I have a finite number of UIImageViews that are subviews of a larger view. I can manually check for collision and set the background colors of the UIImageViews accordingly. The problem is that this creates a rather large if / else block that I can see growing out of control. In JavaScript I can use a for loop and eval() to evaluate a string + i (the loop var) and then simply call the new var.backgroundColor = someColor;
It seems that eval is not used in OBJ C, so I am looking for an equivalent that will allow me to create a loop that executes the same block of code on a given object that only differs from other objects by name only:
Example:
if (someStatement == true){
object_01.someProperty = newProperty;
} else if (someOtherStatement == true){
object_02.someProperty = newProperty;
} else if (someOtherOtherStatement == true){
object_03.someProperty = newProperty;
}
I want to write the above as a loop. I realize I can use an Array, but if so, how? Also, I want to loop through CGRects which are not storing properly in an Array.
Any help greatly appreciated!
Damon
Creating your array:
NSMutableArray *items = [NSMutableArray array];
Populating your array with CGRects:
[items addObject:[NSValue valueWithCGRect:frame]];
Pulling a CGRect from the array:
CGRect frame = [[items objectAtIndex:0] CGRectValue];
Replacing an index in the array:
[items replaceObjectAtIndex:0 withObject:[NSValue valueWithCGRect:frame]];
The best way is to put object_01, object_02, ... into an array and then use `-[NSArray objectAtIndex:] to access the objects.
You could also use Key-Value Coding:
NSString *keyPath = [NSString stringWithFormat:#"object_0%d.someProperty", objectIndex];
[self setValue:newProperty forKeyPath:keyPath];
But as rpetrich mentioned, it's not good programming style.
I am creating an NSArray with CGRects using the following line:
[self setMyArray:[NSArray arrayWithObjects:[NSValue valueWithCGRect:CGRectMake(x,y,z,a)], [NSValue valueWithCGRect:CGRectMake(x,y,z,a)], nil]]
I am then trying to update the CGrects in the Array every so often like this:
for (NSValue *bound in myArray)
{
CGRect myRect = [bound CGRectValue];
myRect.origin.y += 2;
}
However this is not working, when the loop runs again, the origin is still the same. I am assuming this has something to do with the NSValue wrapper?, What can I do so the value is actually updated in the array?. Thank you.
-Oscar
NSValue objects are immutable so there is nothing you can do. You should use a mutable array and replace the existing object with a new NSValue instance containing the new rect.
As others mentioned, NSValue instances are not mutable so you will have to recreate them and then replace them in an NSMutableArray.
Another option is to use more 'classic' C memory management (malloc/free) and manage your array that way. There is nothing wrong with going that route. You just have to be more careful as it is not has high level as the Objective-C/Foundation APIs.
// Create an array to hold 100 rectangles
CGRect* rectangles = (CGRect*) malloc(100 * sizeof(CGRect));
// Change the rectangle at index 2 (remember arrays are 0 based, so this is the 3rd)
CGRect* rect = &rectangles[2];
rect->size.width /= 2.0;
// Shortcut for accessing rectangles
rectangles[2].size.width = 100.0;
// Get rid of the array
free(rectangles);
Might be faster too if you work on for example a game.