How can I programmatically spawn an unlimited number of UIImageViews on to the screen in a random location on a time increment. The random location would be:CGPoint(arc4random() % (481), arc4random() % (321),20,20) I just don't understand how to constantly create new ones with one image variable and a for loop. Or an NSTimer that on every increment adds a new image to the screen.
I know there are some tutorials out there for this, but the only ones I could find used automatic reference counting. It should be simple to create, and a link is fine.
Other questions:
UI is for user-interface, but since i'm not using a storyboard or xib, would there be a better type of imageView to use.
How about something like this, in your view controller class (substituting your own image file name of course):
- (void)viewDidLoad {
[super viewDidLoad];
[NSTimer scheduledTimerWithTimeInterval:.5 target:self selector:#selector(addViews:) userInfo:nil repeats:YES];
}
-(void)addViews: (NSTimer *) aTimer {
UIImageView *iv = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"New_PICT0011.jpg"]];
CGRect rect = CGRectMake(arc4random() % (481), arc4random() % (321), 20, 20);
[iv setFrame:rect];
[self.view addSubview:iv];
}
You would have to put some kind of counter in there, and when it got to the total number of views you wanted, call [aTimer invalidate];
Related
Background:
I am using x-code version 3.1.4 and an iphone simulator version 3.1.
Right now I am building a game where a car (portrayed by a UIImageView) moves left and right using two buttons (left and right). That part works fine. However, I am trying to randomly spawn UIImageViews at the top that will go down at a constant rate. I am able to get one UIImageView to move using this:
-(void)moveMeteor{
iv.center=CGPointMake(iv.center.x, iv.center.y + 1);
[self performSelector:#selector(moveMeteor) withObject:iv afterDelay:0.01];
}
where iv is a UIImageView and I call this function when I click a button. However, I want to create randomly spawning UIImageViews. So, I did this using this code:
-(void)addViews: (NSTimer *) aTimer {
UIImageView *iv = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"Meteor.png"]];
CGRect rect = CGRectMake(arc4random() % (350), arc4random() % (5), 35, 35);
[iv setFrame:rect];
[self.view addSubview:iv];
}
I call this function by clicking a start button which does this:
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(addViews:) userInfo:nil repeats:YES];
I thought about using tags by changing the addViews function to :
-(void)addViews: (NSTimer *) aTimer {
UIImageView *iv = [[UIImageView alloc]initWithImage [UIImage imageNamed:#"Meteor.png"]];
CGRect rect = CGRectMake(arc4random() % (350), arc4random() % (5), 35, 35);
[iv setFrame:rect];
[self.view addSubview:iv];
iv.tag=tagStart;
[self moveMeteor:tagStart];
tagStart = tagStart+1;
}
(the initial value of tagStart is 1);
and changing the move function to:
-(void)moveMeteor:(int)tag{
UIImageView *test= (UIImageView *)[self.view viewWithTag:tagStart];
test.center=CGPointMake(test.center.x, test.center.y + 1);
[self performSelector:#selector(moveMeteor) withObject:tagStart afterDelay:0.01];
[test release];
I am very confused on how to resolve this issue. The problem is either that my move function cannot handle so many moving things at a time, my tag system is faulty, my *test is faulty, or that I am calling to many functions at the same time, or something else. Please help and give me a solution to this annoying problem.
A better option than performSelector is NSTimer, and a better option again is CADisplayLink. You limit of performance will probably be using UIImageView and the associated overhead with moving and redrawing them all, so you may want to change to use CALayers instead. But, change one thing at a time.
For the meteors, I'd hold them all in an array and then each time your moveMeteor method is called, iterate through the array and move each one. Your tag method could work but will be less efficient (you're missing a loop to iterate through the tags basically).
-(void)moveMeteors {
for (UIImageView *test in self.meteors) {
test.center = CGPointMake(test.center.x, test.center.y + 1);
}
}
If you have an array called meteors that you add your new meteors to, and a timer calls moveMeteors periodically.
I am working in iPhone application, Using UITextView to show multiple text inside it, when the user tab the screen, automatically scroll UITextView content from bottom to top (scrolling level slow) and its working fine, i want to increase scroll speed like 1,2,3,4,5,6...10, How to do this? Please help me.
Thanks in advance
I tried this:
- (void)viewDidLoad
{
[super viewDidLoad];
ScriptDetails = [[UITextView alloc]initWithFrame:CGRectMake(0,9,480,285)];
ScriptDetails.text=Script;
ScriptDetails.textAlignment=UITextAlignmentLeft;
ScriptDetails.editable=NO;
ScriptDetails.delegate=self;
ScriptDetails.font=[UIFont fontWithName:str1 size:f];
ScriptDetails.backgroundColor=tColor1;
ScriptDetails.textColor=tColor;
[self.view addSubview:ScriptDetails];
UITapGestureRecognizer *singleTap = [[[UITapGestureRecognizer alloc] initWithTarget: self action:#selector(TouchToPlay)] autorelease];
singleTap.delegate=self;
singleTap.numberOfTapsRequired = 1;
[ScriptDetails addGestureRecognizer:singleTap];
}
-(void)TouchToPlay
{
NSLog(#"single");
timer = [NSTimer scheduledTimerWithTimeInterval:(35.0/1000.0)
target:self
selector:#selector(autoscrollTimerFired:)
userInfo:nil
repeats:YES];
}
- (void)autoscrollTimerFired:(NSTimer*)timer
{
CGPoint scrollPoint = ScriptDetails.contentOffset;
scrollPoint = CGPointMake(scrollPoint.x, scrollPoint.y + 1);
[ScriptDetails setContentOffset:scrollPoint animated:NO];
}
The other way is to put the variable in - (void)autoscrollTimerFired:(NSTimer*)timer method
in scrollPoint = CGPointMake(scrollPoint.x, scrollPoint.y + 1); line instead of just 1 put there some iVar instead and provide some mechanism for like say one + and - button to increase and decrease the speed.
If its just about scrolling fast, you can minimize the time at which the timer function is called. And for scrolling slow, you can increase the time.
Let me know if you are looking for something else.
I've made a program that generates an image at a random x-coordinate at the top of the screen. The image then falls down to the bottom. However, I want to keep generating new images (of the same image) every few seconds so that it's as though these duplicates of the same image are continually "raining" from the top. (Note: Eventually, as I continue to develop this app, I will need to recall the location of each image at any moment, so I believe I will need each spawned image to be part of an array. I also believe I must move each image step-by-step, so I cannot rely on animation).
The problem is: How can I make all of this code repeat every 0.5 seconds so that each newly spawned image has its own moveObject timer. It will be like raindrops falling from the top.
#implementation ViewController {
UIImageView *_myImage;
}
- (void)viewDidLoad
{
srand(time(NULL));e
int random_x_coordinate = rand() % 286;
CGRect myImageRect = CGRectMake(random_x_coordinate, 0.0f, 40.0f, 40.0f);
UIImageView *myImage = [[UIImageView alloc] initWithFrame:myImageRect];
[myImage setImage:[UIImage imageNamed:#"flake.png"]];
myImage.opaque = YES;
[self.view addSubview:myImage];
_myImage = myImage;
//FALLING BIRDS TIMER
moveObjectTimer = [NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:#selector(moveObject) userInfo:nil repeats:YES];
}
//FALLING BIRDS MOVER
-(void) moveObject { // + means down and the number next to it is how many pixels y moves down per each tick of the TIMER above
_myImage.center = CGPointMake(_myImage.center.x, _myImage.center.y +1);
}
You have different solutions:
Just 1 timer in which you update an array of UIImageView (I prefer this one)
Subclass Imageview and put the timer inside the class so each one would have its own timer
Maybe you can use [UIView animateWithDuration... instead of timers, but not sure if you can use many at the same time.
The problem is: How can I make all of this code repeat every 0.5
seconds so that each newly spawned image has its own moveObject timer.
Look into Core Animation's particle effects -- they're not just for smoke, fog, and fire. By setting up a particle emitter and creating particle cells using your image, you can have Core Animation take care of the whole operation. I don't see much official documentation on the topic, but you can read the reference page for CAEmitterLayer to get an idea of how it works, and then take a look at the tutorial on raywenderlich.com.
Use this function for each raindrop object.Just provide point to move to:
-(void)raindropAnimation:(CGPoint)dropPoint
{
raindrop.frame = CGRectMake(dropPoint.x,0,raindrop.frame.size.width,raindrop.frame.size.height) //here y is 0 ie topmost
[UIView animateWithDuration:2.0
delay:0.0
options:UIViewAnimationCurveEaseInOut
animations:^ {
raindrop.frame = CGRectMake(dropPoint.x,dropPoint.y,raindrop.frame.size.width,raindrop.frame.size.height) //will move to point
}
completion:^(BOOL finished) {
[self perfromSelector:#selector(raindropAnimation:CGPointMake(xnewpointhere,ynewpointhere))];
}];
I'm fairly new to Objective-C development.
In my first app built from scratch, I'd like to draw a circle that increases its size as time runs.
The problem is that I've been able to move the circle through the screen, but can't seem to update its properties so it gets bigger or smalles.
Thing goes like this:
CCircle class
#interface CCircle : CShapes
#property float radius;
#property float startAngleRadians;
#property float endAngleRadians;
In the first view controller (ShapesViewController.m):
- (void) viewDidLoad
{
(...)
CCircle* circle = [[CCircle alloc] initWithFrame:frame];
circle.radius = 40;
circle.startAngleRadians = M_PI;
circle.endAngleRadians = 2*M_PI;
circle.tag = 1;
[self.view addSubView:circle];
//I also schedule an update method, so that it is called every 1 seconds.
[NSTimer scheduledTimerWithTmieInterval:1.0 target:self selector:#selector(updateCircle) userInfo:nil repeats:YES];
}
Also in ShapesViewController.m, the update method:
- (void) updateCircle
{
CCircle *circle = [self.view viewWithTag:1];
//Now here: if I do this: the circle will "move" through the screen
CGRect frame = circle.frame;
frame.origin.x += 5;
[circle setFrame:frame];
//However, if I try to change the circle properties, I don't know what to do so
//that affects the circle. In this case, the circle will move through the screen,
//but I keep seeing always the same size(radius).
circle.radius += 5;
//I've tried the following (of course, not all at the same time):
//[self.vew addSubview:circle];
//[self.view sendSubviewToBack:circle];
//[self.view sendSubviewToFront:circle];
//[self.view setNeedsDisplay];
//[self.view setNeedsLayout];
}
Any help on what am I doing wrong, and what should I do to achieve what I want?
Thanks!
You updated a property of circle, not self.view, so you need to call setNeedsDisplay on circle.
After drawing the circle, you have to update your view.
[self.view setNeedsDisplay];
I think that'll do it.
here is my code :
-(void) createNewImage {
UIImage * image = [UIImage imageNamed:#"boutonplay_03.png"];
imageView = [[UIImageView alloc] initWithImage:image];
[imageView setCenter:[self randomPointSquare]];
[[self view] addSubview:imageView];
ix=imageView.center.x;
iy=imageView.center.y;
X=(240-ix)/230;
Y=(160-iy)/230;
}
-(void)Animation{
imageView.center = CGPointMake(imageView.center.x + X, imageView.center.y + Y);
}
"CreateNewImage" is alled every 2 seconds, and there is a CADisplayLink on "Animation".
My problem is that the animation of "imageView" stops every 2 seconds because a new "imageView" is created and this last start moving and it stops again after 2 seconds, and again and again and again ... What I would like is to make "imageView" continue his animation in spite of the creation of new "imageView".How can I do this please ? sorry for my english I'm french:/
Check to see if imageView == nil or not.
if (imageView == nil) {
imageView = [[UIImageView alloc] initWithImage:image];
}
//rest of code
You are creating a new UIImageView every time you call CreateNewImage
instead, check to see if it has already been created.
-(void) createNewImage {
if(imageView == nil) {
UIImage * image = [UIImage imageNamed:#"boutonplay_03.png"];
imageView = [[UIImageView alloc] initWithImage:image];
}
[imageView setCenter:[self randomPointSquare]];
[[self view] addSubview:imageView];
ix=imageView.center.x;
iy=imageView.center.y;
X=(240-ix)/230;
Y=(160-iy)/230;
}
If I understand correctly your case, what happens is this:
you create a new image in createNewImage;
this image is animated through the method:
-(void)Animation{
imageView.center = CGPointMake(imageView.center.x + X, imageView.center.y + Y);
}
which animates your imageView ivar;
when you create a new image, after 2 seconds, you reassign it to the imageView ivar, so that the Animation method can animate it when it is called;
at the same time, you have lost the reference to your previous image that you were animating before, so Animation will not animate it anymore.
Now what I suggest is trying to change Animation in this way:
-(void)Animation{
for (UIView* imgView in self.view.subviews) {
imgView.center = CGPointMake(imgView.center.x + X, imgView.center.y + Y);
}
}
So, this will simply animate any subview of your main view. If your only subviews are the images you want to animate, it should be fine.
EDIT:
From your description in the comments, it seems to me that what happens is that addSubview has the effect of stopping shortly all animations. I don't know if this is related to loading the images from disk, but I remember I also experienced a similar problem time ago. The only approach I see is pre-creating and pre-adding all the UIImageViews you need (or a maximum number of allowed images), make them all hidden and then making them visible again one by one. This would make the logic of your program more complex, but it would work.
Another approach could be using CALayers instead of UIVIews. Best thing would be using a CABasicAnimation to animate your images.
If you want to use UIImageView, you could get [imageView layer] and add this one instead of the view, like this:
[[self.view layer] addSublayer:[imageView layer]];
but I am not sure that this approach would work.
Hope that his helps.