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))];
}];
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 new in iOS developement.When I press the stopwatch start button I want to display the timer like counter token effect.I have attached image for your reference.I have done to display secs and minutes but I dont know, How animate autoscroll effect? How can I do this?
When the counter is moving it shouldn't jump from one number to another number, instead it should move from one number to next number smoothly, just like the petrol pump meter. Thanks
I have done something like this before - this code is not necessarily clean, but it does the job.
You want to create twoUILabels in your .h file:
IBOutlet UILabel *figureLabel1;
IBOutlet UILabel *figureLabel2;
Then you want to create a BOOL so that we can tell which UILabel is visible to the user.
BOOL firstLabel;
So lets imagine that the initial label (showing the number 1) is figureLabel1 and the future UILabel to be displayed (showing the number 2) is figureLabel2. However when the counter adds one, then the figureLabel2 moves up and takes the figureLabel1's place. Then, while the figureLabel1 is hidden, it takes the place of figureLabel2 when figureLabel1 was visible.
See here:
// Check what label is showing
if (firstLabel == YES) {
// Following code the hide and move the figureLabel1
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelegate: self]; //or some other object that has necessary method
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
// Slowing fade out the figureLabel1
[figureLabel1 setAlpha:0];
// Move the figureLabel1 up and out of the way
[figureLabel1 setFrame:CGRectMake(20, 108, 287, 55)];
[UIView commitAnimations];
// Following code the show and move the figureLabel2
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
// Slowing fade in the figureLabel2
[figureLabel2 setAlpha:1];
// Move the figureLabel2 up and into position
[figureLabel2 setFrame:CGRectMake(20, 141, 287, 55)];
[UIView commitAnimations];
// Update BOOL for next label
firstLabel = NO;
} else {
// Exactly the same but opposite
}
As I said, this is not pretty but it shows the basic concept. All the best!
You need to manually scroll tableView instead of scrollToRowAtIndexPath because this animation uses its own timer interval and its very difficult or we can say impossible to change its time interval.
So, I am Implementing an API for such kind of problems and made a demo app for you with smooth scrolling as you want.
You need to use an outer timer that fires every 1 second and an internal timer that will fire every 0.03 sec as my tableRow Height is 30 I calculated internal timer interval as :---
Move 30 pixels for 1 sec , then
Move 1 pixel for 0.33 sec inside internal timer.
The Internal timer will invalidate and fire every 1 second as initialized within outer timer.
And internal timer will perform the movement of cell.
dial4 is a tableView
Have a look at my code.
#define rowHeight 30
-(void)startCounter
{
outertimer=[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(snapCell) userInfo:nil repeats:YES];
}
-(void)stopCounter
{
[outertimer invalidate];
}
-(void)snapCell
{
NSLog(#"Initialize Internal timer %i",secLsb);
secLsb++;
if (secLsb==10) {
[dial4 setContentOffset:CGPointMake(0, 0) animated:NO];
secLsb=0;
NSLog(#"dial content offset y is %f",dial4.contentOffset.y);
}
[internaltimer invalidate];
internaltimer=[NSTimer scheduledTimerWithTimeInterval:0.03
target:self
selector:#selector(automaticScroll)
userInfo:nil
repeats:YES];
}
-(void)automaticScroll
{
NSLog(#"val is & y ======== %f",dial4.contentOffset.y);
[dial4 setContentOffset:CGPointMake(dial4.contentOffset.x,dial4.contentOffset.y+1) animated:NO];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return rowHeight;
}
Have a look at Time Counter
From image it can be assume that you are trying to get an effect like count-down timer. Have a look at countdown timer here. It may helps you somehow.
Enjoy Programming !!
Refer both links for your output. It helps you
UIPickerView that looks like UIDatePicker but with seconds
and
How can I make my UIPickerView show labels after the selected value?
Thanks.
Several good ideas here so far, but I'll add another.
First, be sure to set the container view (your blue rectangle) to clip children, using the Clip Subviews checkbox in Interface Builder.
Second, add a set of child views with images of each numeral for each digit to be presented (4 * 10 = 40 child views in your case). Use tags in IB so you can easily access each one. Set the initial bounds to just below your bottom margin.
Call UIView animateWithDuration. In the animations block, set the new digit view's frame to appear in the clipped parent view's bounds, and set the old digit view's frame to just above the container's bounds. Since both view's frame changes are animated in the same block, this will create the effect of the new digit sliding up into place as the old digit slides out the top.
In the completion block, set the old digit's frame back to the original position below the container view.
With this approach, you can play with the duration and the timing curves, so that you cam emulate the pause that occurs with each digit fully displayed between transitions.
A similar approach can also be used with CALayer, or with sprites, but UIViews are lightweight, perform well, and easiest to assemble in Interface Builder.
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];
Here is my code :
-(void)collision {
if(CGRectIntersectsRect(ball.frame,center.frame)) {
center.alpha=0.1;
}
}
-(void)viewDidLoad {
[super viewDidLoad];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:7.0f];
[ball setCenter:CGPointMake(200, 100)];
[UIView commitAnimations];
[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(collision) userInfo:nil repeats:YES];
}
My problem is that when viewDidLoad "center.alpha=0.1" but "center" and "ball" have not collided yet , I don't know why, I think it is due to the animation.
Although the animation takes 7 seconds, [ball setCenter:CGPointMake(200, 100)];is set immediately and because of that - (void)collision probably sets your alpha to 0.1 before "ball" intersects with "center" in the animation.
Instead of UIView animations you could use a NSTimer to slowly change the center of "ball".
You are scheduling the call to collision 0.01 seconds after the line is executed at the end of viewDidLoad. But the view hasn't been displayed yet and so it could take longer than 0.01 seconds to display the view.
Try viewDidAppear
Having said that, I think you are not clear on the purpose of the animtions in iOS. These are not for calculating collision detection - they are just for moving things from a start point to an end point over a set time. I would suggest you read the Apple docs on the animation system.
I'm trying to animate a button which moves around the screen. At any point, the user can push the button. But the button doesn't respond to touches. I've tried an animation block but the button simply moves immediately to its end coordinates, while the frame shows the animation (the button is called bubble):
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:5.0];
CGAffineTransform newTransform = CGAffineTransformMakeScale(3, 3);
bubble.transform = CGAffineTransformTranslate(newTransform, 0, -460);
[UIView commitAnimations];
So then I tried Core Animation with the help of some sample code (the path isn't important, it's just an example):
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath,NULL,15.0f,15.f);
CGPathAddCurveToPoint(thePath,NULL,
15.f,250.0f,
295.0f,250.0f,
295.0f,15.0f);
CAKeyframeAnimation *theAnimation=[CAKeyframeAnimation animationWithKeyPath:#"position"];
theAnimation.path=thePath;
CAAnimationGroup *theGroup = [CAAnimationGroup animation];
theGroup.animations=[NSArray arrayWithObject:theAnimation];
theGroup.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
theGroup.duration=15.0;
CFRelease(thePath);
[bubble.layer addAnimation:theGroup forKey:#"animatePosition"];
But the buttons still don't respond to touches. Btw, I have several of these 'bubble' buttons on screen at once so having several NSTimers concurrently active wouldn't be optimal.
Can anyone suggest another approach? Should I perhaps animate UIImageViews and make them repsond to touches? Or will I have the same problem? This has been puzzling me for a couple of days so any help much appreciated.
Thanks :)
Michael
When animating a GUI element like the UIButton, the actual position only changes when the animation is done. This means any touch events during the animation will only work at the starting point of the button. You can get the current displayed position of the button by accessing it's presentationLayer. You'll probably need to implement your own event handling, look at the position when the user touches the screen, and compare that to the bounds you get from the presentationLayer.
Well, for anyone interested, here's my solution which works perfectly well:
- (void)startMinigame {
makeBubbles = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:#selector(generateRandomBubble) userInfo:nil repeats:YES];
floatBubbles = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0 target:self selector:#selector(floatBubbles) userInfo:nil repeats:YES];
}
- (void)generateRandomBubble {
//Get a random image for the bubble
int randomIndex = arc4random()%kNumberBubbles;
UIImage *bubbleImage = [bubbleImages objectAtIndex:randomIndex];
//Create a new bubble object and retrieve the UIButton
Bubble *bubble = [[Bubble alloc]initWithImage:bubbleImage andIndex:randomIndex];
UIButton *bubbleButton = bubble.bubbleButton;
//Configure the button
[bubbleButton addTarget:self action:#selector(bubbleBurst:) forControlEvents:UIControlEventTouchDown];
//Add the bubble to the scene
[self.view insertSubview:bubbleButton belowSubview:bathTub];
//Add the bubble to an array of currently active bubbles:
[self.bubbles addObject:bubble];
[bubble release];
}
- (void)floatBubbles {
//Move every active bubble's frame according to its speed.
for (int i = 0; i < [bubbles count]; ++i) {
Bubble *bubble = [bubbles objectAtIndex:i];
int bubbleSpeed = bubble.speed;
CGRect oldFrame = bubble.bubbleButton.frame;
CGRect newFrame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y - bubbleSpeed, oldFrame.size.width+0.1, oldFrame.size.height+0.1);
bubble.bubbleButton.frame = newFrame;
if (bubble.bubbleButton.frame.origin.y < -100.0) {
[bubbles removeObject:bubble];
[bubble.bubbleButton removeFromSuperview];
}
}
}
This sounds a bit too complicated for UIKit and CoreAnimation. It seems like you're developing something like a game. Why not use a global animation timer for let's say ~60 fps and do your drawing in Quartz2D? Then for each touch you could quickly check any hits between Quartz refreshes.
Try animating the uiview's frame property.