I need a way of creating a UIView dynamically. Thus, the parent class could look for say a count of array items, and create UIViews on the fly of the amount of items in the array.
The views need to be allocated dynamically, do I can't create them on the fly.
Can you help?
You can create them anytime.
UIView *myView = [[UIView alloc] initWithFrame:myFrameRect]
You can call this from within a loop, store the pointer in another array for further use or whatever you want. Just remember to release them when you are done. Otherwise you will be in a memory issue.
I think what you want is this (according to the response on taskinoor answer).
NSMutableArray* array = [[NSMutableArray alloc] init];
for (int i =0; i < 10; i++){
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 5.0 + (i * 20), 15.0, 15.0)];
[array addObject:myView];
}
When when you want one of those views just call
UIView* view = (UIView*)[array objectAtIndex:number];
Related
This question already exists:
iphone-non-global uiimageview detection
Closed 8 years ago.
Background:I am using XCode 3.1. Please do not comment about this.
Problem: I have two buttons, fire and make meteor, which make UIImageViews when clicked. I add these UIImageviews to two seperate NSMutable arrays, bullets and meteors. How would I check for collisions between any element in the bullet array and an element in the meteor array. Also, if there were a collision, how would I then remove the bullet and the meteor in question from the view. Thank you. Here is my code so far:
-(IBAction)createBullets:(id)sender{
UIImageView *two = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"lazerBeam.png"]];
CGRect rectTwo = CGRectMake((image.center.x), (image.center.y - 45), 7, 20);
[two setFrame:rectTwo];
[self.view addSubview:two];
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(moveBulletOne:) userInfo:two repeats:YES];
[bulletImageViews addObject:two];
}
-(IBAction)createMeteors:(id)sender{
UIImageView *one = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"Meteor.png"]];
CGRect rectOne = CGRectMake(arc4random() % (310), arc4random() % (1), 35, 35);
[one setFrame:rectOne];
[self.view addSubview:one];
[NSTimer scheduledTimerWithTimeInterval:0.4 target:self selector:#selector(moveMeteorOne:) userInfo:one repeats:YES];
[meteorImageViews addObject:one];
}
I also move the meteors and bullets using a NSTimer to a move function, but that is irrelevant. Basically, I want to check when a bullet collides with a meteor, and when that happens, to remove the meteor and bullet in question from the view.
Basic O(n^2) search, check every bullet against every meteor. This is probably fine for a small app, but it can be optimized using spatial partitioning methods to reduce the amount of checks required (quadtrees etc). You want to create a "toRemove" array rather than removing in place so that the array is not modified while enumerating.
NSMutableArray* bulletsToRemove = [NSMutableArray array];
NSMutableArray* meteorsToRemove = [NSMutableArray array];
for (UIImageView* bullet in bulletImageViews)
{
for (UIImageView* meteor in meteorImageViews)
{
if (CGRectIntersectsRect(bullet.frame, meteor.frame)
{
[bulletsToRemove addObject:bullet];
[meteorsToRemove addObject:meteor];
break;
}
}
}
[bulletsToRemove makeObjectsPerformSelector:#selector(removeFromSuperview)];
[meteorsToRemove makeObjectsPerformSelector:#selector(removeFromSuperview)];
[bulletImageViews removeObjectsInArray:bulletsToRemove];
[meteorImageViews removeObjectsInArray:meteorsToRemove];
I hope this makes sense...I have an NSMutableArray that I'm attempting to store multiple UIScrollView's in. Each UIScrollView is going to have multiple images and the ultimate goal is to be able to allow the user to swipe vertically for categories (each instance of UIScrollView) and horizontally for each image in that category (each instance of UIImageView). For now, until I get this code to work, I'm just creating 1 UIScrollView with 1 image and I'm adding that to scrollViewManager. This seems to add everything correctly, but when I leave this function, game over. My Array is empty. I don't understand. Am I supposed to do some sort of deep copy when I add to the Array so it doesn't get destroyed. Perhaps I'll figure it out when I wake up tomorrow, but for now, I'd like to break everything in sight. Thanks, in advance!
EDIT: Sorry for lack of details, I posted this rather late. My project is using ARC, so I'm not releasing scrollView anywhere. Here is the declaration for scrollViewManager in my header file:
#property (nonatomic, retain) NSMutableArray *scrollViewManager;
Here is the code I use to retrieve my scrollView:
UIScrollView *test = (UIScrollView*) [self.scrollViewManager objectAtIndex: 0];
And here is the code to initialize the scrollView and array:
scrollViewManager = [[NSMutableArray alloc] initWithCapacity: 1];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
[scrollView setBackgroundColor:[UIColor blackColor]];
[scrollView setCanCancelContentTouches:NO];
scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
scrollView.clipsToBounds = YES; // default is NO, we want to restrict drawing within our scrollview
scrollView.scrollEnabled = YES;
scrollView.pagingEnabled = YES;
NSString *imageName = #"pic1.png";//[NSString stringWithFormat:#"pic%d.jpg", i+1];
UIImage *image = [UIImage imageNamed:imageName];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
// setup each frame to a default height and width, it will be properly placed when we call "updateScrollList"
CGRect rect = imageView.frame;
rect.size.height = 400;
rect.size.width = 300;
imageView.frame = rect;
imageView.tag = 1; // tag our images for later use when we place them in serial fashion
[scrollView addSubview:imageView];
[self.scrollViewManager addObject: scrollView];
There are some things that are kind of vague. For example:
Are you using ARC, if not and you are releasing your scrollView anywhere?
Are you using the auto-create-synthesize thing?
What type of property is your scrollViewManager (weak, strong, etc).
If you are creating your ivars automatically when defining a property, your ivar should be called _scrollViewManager instead of scrollViewManager.
The use of self.scrollViewManager is correct when using this method.
I always do this kind of stuff the other way around. When I have a property like this:
#property (nonatomic, strong) NSMutableArray* myArray;
I always initialize them using the self keyword:
self.myArray = [NSMutableArray new];
And when modifying it, I always use the generated ivar:
[_myArray addObject:myFineObject];
I think this is something I kept using when moving from non-ARC to ARC. Not sure if it still the way to go, but it works like expected.
So I have run into a fairly complex problem with my application.
I have about 40 UIImageView objects that need to be displayed at different locations when different ones are stacked (EX img 1 needs to take img 2 place if img2 ==NO).
What I am trying to do is use a "for loop" and run through all my Boolean values that check if the image is selected.
I was curious if it is at all possible to call variable like this
-(IBAction) button:(id) sender{
scrollView.hidden = YES;
NSInteger i = 1;
(#"imagenumber%d", i) = [[UIImageView alloc] initWithFrame: CGRectMake (28, 230, 86, 26)];
[[imagenumber(#"%d", i)] setImage:[UIImage imageNamed:#"csa.jpg"]];
[self.view addSubview: [imagenumber(#"%d", i)]];}
}
where #"imagenumber%d" is my NSImageView and i is a number ranging from 1-40.
I'm sorry if this is confusing, I really would appreciate any help at all on the subject because I can’t seem to get any idea and have been working on this problem for several weeks.
Put the views in a NSMutableArray - you can access elements in those by index, use fast enumeration, ... E.g.:
UIImageView *image = [[UIImageView alloc] ...
[myMutableArray addObject:image];
// ...
For the sake of hilarity you could also put them in a intrinsic array and use pointer arithmetic. (Which is probably what happens behind the scenes with faster enumeration for those interested)
UIImageView * imageArray[40];
...
for(UIImageView * this = imageArray; this; this++)
{
this = [[UIImageView alloc] initWithFrame: CGRectMake (28, 230, 86, 26)];
...
}
I have created a nib file with different views. I was planning to use this nib file as a template, much like in web (i.e. html). So create a new instance of this nib fill it with actual data, create another new instance of nib & fill with actual data etc...
But when I try to do this, nib instance is not behaving the same. Only one gets created. Am I missing something? Aren't nib files supposed to be used this way?
Here's how I tried to use my nib file -
int yCoord = 0;
for(int i=0; i<[resultSet count]; i++)
{
[[NSBundle mainBundle] loadNibNamed:#"msgView" owner:self options:nil];
UIView *tmpMsgView = [[UIView alloc] initWithFrame:CGRectMake(0, yCoord, msgView.view.frame.size.width, msgView.view.frame.size.height)];
tmpMsgView = msgView.view;
UILabel *senderLabel = (UILabel *)[tmpMsgView viewWithTag:1];
[senderLabel setText:#"trial"];
[self.view addSubview:tmpMsgView];
[tmpMsgView release];
yCoord += msg.view.frame.size.height;
}
this msgView is hooked up to the nib file (through IB) and the owner of that nib file too is defined as this viewController class.
Your code is adding the same instance over and over again, not a new instance each time.
UIView *testView = [[UIView alloc] init];
testView.backgroundColor = [UIColor blueColor];
for (int i=0; i<5; i++) {
testView.frame = CGRectMake(0.0, (i+1)*40.0, 200.0, 20.0);
[self.window addSubview:testView];
}
[testView release];
and
for (int i=0; i<5; i++) {
UIView *testView = [[UIView alloc] init];
testView.backgroundColor = [UIColor blueColor];
testView.frame = CGRectMake(0.0, (i+1)*40.0, 200.0, 20.0);
[self.window addSubview:testView];
[testView release];
}
are two very different things.
the first actually makes very little sense and results in a single blue bar,
the second makes much more sense and results in 5 blue bars -- I think this is what you want.
look at the following, it will create 5 rectangles with all different colors -- this is to illustrate that each is rectangle is its seperate instance loaded from a nib:
for (int i=0; i<5; i++) {
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"aView" owner:self options:nil];
UIView *nibView = [nibObjects objectAtIndex:0];
nibView.backgroundColor = [UIColor colorWithRed:(i+1)*0.14 green:(i+1)*0.11 blue:200.0 alpha:1.0];
nibView.frame = CGRectMake(0.0, (i+1)*40.0, 200.0, 20.0);
[self.window addSubview:nibView];
}
now, if we assign our nibObjects array only once before the loop, we again have the same issue in that we add the same instance over and over again, which will result in one rectangle only:
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"aView" owner:self options:nil];
for (int i=0; i<5; i++) {
UIView *nibView = [nibObjects objectAtIndex:0];
nibView.backgroundColor = [UIColor colorWithRed:(i+1)*0.14 green:(i+1)*0.11 blue:200.0 alpha:1.0];
nibView.frame = CGRectMake(0.0, (i+1)*40.0, 200.0, 20.0);
[self.window addSubview:nibView];
}
hope this helps
You have memory management issues in your code.
UIView *tmpMsgView = [[UIView alloc] initWithFrame:CGRectMake(0, yCoord, msgView.view.frame.size.width, msgView.view.frame.size.height)];
You alloc/init a UIView and on the next line you store some other view in the variable. You lose the pointer to the UIView and therefore you leak its memory.
Whether the code using senderLabel will work depends on what you defined in the nib file.
In [tmpMsgView release]; you're most probably releasing something you do not own (the pointer no longer points to the UIView you alloc/inited). The issue here depends on how you declare msgView. If you are releasing an object you do not own, it might get deallocated and that might be the reason you do not see it in your app.
What you are trying to do is fine - this is what is routinely done for loading table view cells, for example.
If I do this I have a single outlet from the view controller (which is set as files owner which you have done). This will be the topmost object in the nib, eg a UIView. All of your other Nib components are just subviews of this, you can identify with tags or, if your topmost view is a custom view, that can have outlets which are connected appropriately.
When you do loadNibNamed this will set your outlet ivar to the topmost object. You then assign this to a method level variable, and set your ivar back to nil. You can then do what you like with this before adding it as a subview to something else. Next time you load the nib, you get another fresh version.
Hope that makes sense, let me know if I can add anything else. The problem in your code is that you are doing things with msgView.view instead of the top level object.
I'm trying to get multiple Ball objects to form on screen and bounce around. I have it working fine for one, but I am having problems adding more. Ideally, the amount of balls will vary, but for now I am trying to just get two into an array to change the rest of the code.
My problem is that everything compiles and runs fine, but I still only have one ball. This is where I suspect the problem arises.
I also feel like doing this in viewDidLoad is incorrect, but I'm not sure where it actually belongs.
viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
balls = [[NSMutableArray alloc] init];
CGPoint newPos = CGPointMake(5,5);
for(int i = 0; i < 2; i++) {
[balls addObject:[[[Ball alloc] init] autorelease]];
}
[[balls objectAtIndex:1] setPosition:newPos];
[NSTimer scheduledTimerWithTimeInterval:1.0 / 30.0 target:self selector:#selector(onTimer) userInfo:nil repeats:YES];
}
- (void)onTimer
{
for(int i = 0; i < [balls count]; i++) {
Ball *b = [balls objectAtIndex:i];
[b update];
[(BallView *)self.view refresh:b];
}
}
- (void)refresh:(Ball *)aBall {
ball = aBall;
[self setNeedsDisplay];
}
I've added my onTimer, I thought adding the delay in creating the balls would be enough to make this a nonissue. All update does is change the velocity/direction based on the accelerometer/collisions with the edge of the screen.
Indeed both balls should be displayed. Check their coordinates to see if they overlap. On another note, it appears you are leaking the balls array, as well as the ball1 and ball2 objects. You are alloc init'ing them so you have to release them at the end of viewDidLoad, or, if you are using retain properties, just autorelease them when you set them so that they get retained by the properties.
As for where the code should be located, viewDidLoad should be ok, but consider putting the balls array into the init method you call to create your ViewController, since there's probably no way your ViewController could work without that array being set up.