How to add a delay to a loop? - ios

Im attempting add image views to a UIView using this code:
for (int i = 0; i <numberOfImages; i++) {
UIImageView *image = [UIImageView alloc]initWithFrame:CGRectMake(40, 40, 40, 40)];
image.image = [images objectAtIndex:i];
[self.view addSubview:image];
}
This works but the problem is I would like to have a 5 second delay before it adds each image, instead it adds them all at the same time. Can anybody help me out? Thanks.
Example:
5 seconds = one image on screen
10 seconds = two images on screen
15 seconds = three images on screen

It will be more efficient to use an NSTimer.
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:numberOfSeconds
target:self
selector:#selector(methodToAddImages:)
userInfo:nil
repeats:YES];
This will essentially call methodToAddImages repeatedly with the specified time interval. To stop this method from being called, call [NSTimer invalidate] (bear in mind that an invalidated timer cannot be reused, and you will need to create a new timer object in case you want to repeat this process).
Inside methodToAddImages you should have code to go over the array and add the images.
You can use a counter variable to track the index.
Another option (my recommendation) is to have a mutable copy of this array and add lastObject as a subview and then remove it from the mutable copy of your array.
You can do this by first making a mutableCopy in reversed order as shown:
NSMutableArray* reversedImages = [[[images reverseObjectEnumerator] allObjects] mutableCopy];
Your methodToAddImages looks like:
- (void)methodToAddImages
{
if([reversedImages lastObject] == nil)
{
[timer invalidate];
return;
}
UIImageView *imageView = [[UIImageView alloc] initWithFrame:(CGRectMake(40, 40, 40, 40))];
imageView.image = [reversedImages lastObject];
[self.view addSubview:imageView];
[reversedImages removeObject:[reversedImages lastObject]];
}
I don't know if you're using ARC or Manual Retain Release, but this answer is written assuming ARC (based on the code in your question).

You can use dispatch_after to dispatch a block, executed asynchronously that adds the image. Example:
for(int i = 0; numberOfImages; i++)
{
double delayInSeconds = 5.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
{
UIImageView *image = [UIImageView alloc]initWithFrame:CGRectMake(40, 40, 40, 40)];
image.image = [images objectAtIndex:i];
// Update the view on the main thread:
[self.view performSelectorOnMainThread: #selector(addSubview:) withObject: image waitUntilDone: NO];
});
}

Separate your code into a function, and call via NSTimer.
for (int i = 0; numberOfImages; i++) {
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5
target:self
selector:#selector(showImage)
userInfo:[NSNumber numberWithInt:i]
repeats:NO];
And then your function:
-(void) showImage:(NSTimer*)timer {
//do your action, using
NSNumber *i = timer.userInfo;
//Insert relevant code here
if (!done)
NSTimer *newTimer = [NSTimer scheduledTimerWithTimeInterval:5
target:self
selector:#selector(showImage)
userInfo:[NSNumber numberWithInt: i.intValue+1]
repeats:NO];
}
}
userInfo is a convenient way of passing parameters to functions that you need to call (but they do have to be Objects). Also, by using repeats:NO, you don't have to worry about invalidating the timer, and there's no risk of leaving timer running in memory.

also this is best option. Try this
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];

I think you'd be better off with an animation
for (int i = 0; i < numberOfImages; i++)
{
// Retrieve the image
UIImageView *image = [[UIImageView alloc] initWithFrame:CGRectMake(40, 40, 40, 40)];
image.image = [images objectAtIndex:i];
// Add with alpha 0
image.alpha = 0.0;
[self.view addSubview:image];
[UIView animateWithDuration:0.5 delay:5.0*i options:UIViewAnimationOptionAllowUserInteraction animations:^{
// Fade in with delay
image.alpha = 1.0;
} completion:nil];
}
Not exactly what you asked for, since all the views will be added immediately, and then faded-in, but I feel that you're actually trying to achieve that, like some sort of stacking of images, right?
In fact, if you plan on removing the previous image, you can do it in the completion block, like this:
UIImageView *imagePrevious = nil;
for (int i = 0; i < numberOfImages; i++)
{
// Retrieve the image
UIImageView *image = [[UIImageView alloc] initWithFrame:CGRectMake(40, 40, 40, 40)];
image.image = [images objectAtIndex:i];
// Add with alpha 0
image.alpha = 0.0;
[UIView animateWithDuration:0.5 delay:5.0*i options:UIViewAnimationOptionAllowUserInteraction animations:^{
// Add and fade in with delay
[self.view addSubview:image];
image.alpha = 1.0;
} completion:^(BOOL finished)
{
if (finished && imagePrevious)
{
[imagePrevious removeFromSuperview];
}
}];
imagePrevious = image;
}

Related

UIpagecontrol Need Animation Rigth to Left

Using UIControlpage in my application. load the image and display in page control successfully. but i want to display the images animation like right to left move. Using timer call page control and increase the array count.
//// using NSTimer to call function to show the animation.
[NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:#selector(loadNextController)
userInfo:nil
repeats:YES];
ScrollViewPage = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 20, frameX, frameY)];
ScrollViewPage.pagingEnabled = YES;
ScrollViewPage.backgroundColor=[UIColor redColor];
ScrollViewPage.delegate = self;
ScrollViewPage.backgroundColor = [UIColor clearColor];
ScrollViewPage.contentSize = CGSizeMake(frameX, frameY);
ScrollViewPage.showsHorizontalScrollIndicator = NO;
ScrollViewPage.pagingEnabled = YES;
pageControl = [[UIPageControl alloc] init];
pageControl.frame = CGRectMake(100,self.view.frame.size.height-100,self.view.frame.size.width-200,100);
pageControl.numberOfPages = [_pageImages count];
pageControl.currentPage = 0;
[self.view addSubview: ScrollViewPage];
for(int i = 0; i < [_pageImages count]; i++)
{
NSURL *url = [NSURL URLWithString:[_pageImages objectAtIndex:i]];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
UIImage *tmpImage = [UIImage imageWithData:data];
_backgroundImageView = [[UIImageView alloc] initWithImage:tmpImage];
_backgroundImageView.frame = CGRectMake(frameX * i, 0.0, frameX, frameY);
[ScrollViewPage addSubview:_backgroundImageView];
}
ScrollViewPage.contentSize = CGSizeMake(frameX*[_pageImages count], frameY);
pageControl.backgroundColor=[UIColor orangeColor];
[self.view addSubview:pageControl];
[pageControl addTarget:self action:#selector(pageTurn:) forControlEvents:UIControlEventValueChanged];
///// Here call the timer function
- (void)loadNextController
{
pageControl.currentPage = (pageControl.currentPage+1)%self.pageImages.count;
ScrollViewPage.contentOffset = CGPointMake(pageControl.currentPage * self.view.bounds.size.width, 0);
}
Here i need to show left to right move in uipagecontrol. help me..
In general you should always write what is your current result, what is the expected result and what have you tried.
I can only guess the issue you are facing is there are no animations.
The scroll view has a method setContentOffset:animated which you can use to animate the transition. Another approach is to use UIView animateWithDuration and sett the offset in the method block.
Try any or both of these...
[ScrollViewPage setContentOffset:CGPointMake(pageControl.currentPage * self.view.bounds.size.width, 0) animated:YES];
Or
[UIView animateWithDuration:0.2 animations:^{
ScrollViewPage.contentOffset = CGPointMake(pageControl.currentPage * self.view.bounds.size.width, 0);
}];
EDIT: Loop images.
To loop images you really only need 2 images on the scroll view at the time. I will show you the concept with 3 and you can try and play around with it.
NSArray *allImages;
NSInteger currentIndex = 0;
UIScrollView *scrollView;
UIImageView *imageViews[3];
- (void)begin {
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width*3.0, scrollView.frame.size.height); // always have 3 images at the time
scrollView.contentOffset = CGPointMake(scrollView.frame.size.width, 0.0); // put to center
for(int i=0; i<3; i++) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(scrollView.frame.size.width*i, 0.0, scrollView.frame.size.width, scrollView.frame.size.height)];
[scrollView addSubview:imageView];
imageView[i] = imageView;
}
[self layoutForIndex:currentIndex];
}
- (void)nextImage {
currentIndex++;
[self layoutForIndex:currentIndex];
scrollView.contentOffset = CGPointZero;
[scrollView setContentOffset:CGPointMake(scrollView.frame.size.width, 0.0) animated:YES]
}
- (void)layoutForIndex:(NSInteger)index {
if(allImages.count > 0) { // we need at least one image to do something
for(int i=0; i<3; i++) {
// do the circling
NSInteger imageIndex = index-1+i;
while(imageIndex < 0) imageIndex+=allImages.count;
while(imageIndex >= allImages.count) imageIndex-=allImages.count;
imageViews[i].image = imageViews[imageIndex];
}
}
}
But in general for what you are doing you do not even need a scroll view. On next you can simply add another image view next to the current one and then use the UIView animation to reposition both of the image views.

how to do the animate the uiimageview and stop the remaining process

I am doing one application.In that i am doing the animation for uiimageview to show the different image like below
UIImageView * fearthersanimate = nil;
[fearthersanimate setCenter:sender.center];
fearthersanimate = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 120,200)];
[fearthersanimate setCenter:CGPointMake(sender.center.x, sender.center.y)];
fearthersanimate .animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"water_can_1.png"],
[UIImage imageNamed:#"water_can_2.png"],[UIImage imageNamed:#"water_can_3.png"],
[UIImage imageNamed:#"water_can_4.png"],[UIImage imageNamed:#"water_can_5.png"],
[UIImage imageNamed:#"water_can_6.png"],[UIImage imageNamed:#"water_can_7.png"],
[UIImage imageNamed:#"water_can_8.png"],
nil];
fearthersanimate.animationDuration = 1.0f;
fearthersanimate.animationRepeatCount = 1;
[self.view addSubview: fearthersanimate];
[fearthersanimate startAnimating];
But remaining operation is starting before end of this animation.But i need to do this animation first and until i need to stop the remaining process.
You can call your rest of operation in other method and call it after the same delay as you set animation time for images.
Below is the line of code by which you can call other method after some time interval
[self performSelector:#selector(restOfOperations) withObject:nil afterDelay:1.0];
//restOfOperations method defination/implementation
-(void)restOfOperations
{
//your code you want to perform after image animation
}
You can use the UIView animation block. For example:
[UIView animateWithDuration:0.5 animations:^{
} completion:^(BOOL finished) {
}];
Put the remaining process in the completion block.
Those animations are performed asynchronously, so you just need to fight async with async.
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(feathersanimate.animationDuration * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// Remaining logic
});

Can't trigger function from NSTimer's selector

So here's my code :
- (IBAction)run:(id)sender {
animationPointer = 0;
self.animationArray = [[NSMutableArray alloc] init];
UIView *allViews = [[UIView alloc]
initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
for (int i = 0; i < [self.paths count]; i++) {
[allViews addSubview:[self createAnimation:[self.paths objectAtIndex:i]]];
[self.animationArray addObject:allViews];
}
timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(animateTurn)
userInfo:nil repeats:YES];
}
- (void)animateTurn {
UIView *tempView = [self.animationArray objectAtIndex:animationPointer];
[self.view addSubview:tempView];
for (UIImageView *view in tempView.subviews) {
[[tempView.subviews objectAtIndex:animationPointer] startAnimating];
}
animationPointer++;
if (animationPointer >= [self.animationArray count]) {
[timer invalidate];
}
}
createAnimation returns, as you would expect, a fully animated UIImageView. The plan is to have several on each UIView and then animate them all on a UIView then the next after half a second and it repeats. There's only one on each UIView for now.
However, when calling animateTurn on the NSTimer, the animations don't show up.
If I call the method via the normal [self animateTurn] then they appear but nothing with NSTimer nor the performSelector function.
Any help with this (or even suggestions for a better method)?
EDIT:
I should mention the function is actually being triggered and the code is being run including the startAnimating (checked with BOOL/NSLog). There is simply nothing being shown.
This is Your Solution:-
This is what I used and now the selector is Working. :-)
if (![NSThread isMainThread])
{ [self performSelectorOnMainThread:#selector(selectorToRunInMainThread) withObject:nil waitUntilDone:NO];
}
-(void)selectorToRunInMainThread
{
NSString *str = #"Timer event has fired";
NSTimer *atimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateModel:) userInfo:str repeats:YES];
}
Include this line after your timer:-
[timer fire];

Play Multiple animation in sequence

I am trying to execute ball blasting effect to play sequentially. One after another.
What I have done yet:
For ball blasting effect i had used
UIButton *ballButton = (UIButton *)[cell viewWithTag:10];
ballButton.imageView.animationImages = [[NSArray alloc] initWithObjects:
[UIImage imageNamed:#"1.png"],
[UIImage imageNamed:#"2.png"],
[UIImage imageNamed:#"3.png"],
[UIImage imageNamed:#"4.png"],
nil];
ballButton.imageView.animationDuration = 1;
ballButton.imageView.animationRepeatCount = 1;
and this line on code is attached to multiple buttons in a cell of a collection view.
I call these ballbutton.imageview start the animation like this
[UIView animateWithDuration:1 delay:5 options:UIViewAnimationOptionCurveEaseOut animations:^{
NSIndexPath *path2 = [NSIndexPath indexPathForRow:x inSection:0];
UICollectionViewCell *cell = [ballContainer cellForItemAtIndexPath:path2];
UIButton *ballObject = (UIButton *) [cell viewWithTag:10];
[ballObject.imageView startAnimating];
} completion:^(BOOL b){
NSLog(#" here i call next animation of ball blast to execute ");
}];
I nested 3 animations buttons like this.
First of all, why don't you create one big animation for all other three? The thing with the UIView animateWithDuration: is that it performs the animation block in the period of time you gave to it, i.e. setting the frame from (200,200) to (0,0) will move it proportionally in the time of a second, in your case. But UIImageView's properties regarding animations are made in such way that the animation is already done for you.
Personally, I would suggest using a timer, like this:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:ballObject.imageView.animationDuration
target:self
selector:#selector(performNextAnimation:)
userInfo:nil repeats:NO];
[ballObject.imageView startAnimating];
And in the performNextAnimation method:
- (void) performNextAnimation{
[timer invalidate]; // you have to access the timer you've scheduled with the animation
timer = nil;
/* code for starting the next animation */
}
this way i solved my issue.
initially i started animation by calling this
[self startAnim:index];
than implemented this StartAnim like this and problem solved.
-(void)StartAnim :(int)x{
NSIndexPath *path2 = [NSIndexPath indexPathForRow:x inSection:0];
UICollectionViewCell *cell = [ballContainer cellForItemAtIndexPath:path2];
UIButton *ballObject = (UIButton *) [cell viewWithTag:10];
[ballObject setBackgroundImage:nil forState:UIControlStateNormal];
ballObject.imageView.image = nil;
[ballObject.imageView startAnimating];
double delayInSeconds = 0.15;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if(x-6>=0){
[self StartAnim :(x-6)];
}
});
}

Button image array animation with delay after every cycle

I am working on with a simple animation that I have 10 button and "on click" of one button ( for example 5) all button should start animate one by on but they are animating the same time, please let me know what can be done or ..... Thanks
NSMutableArray* imagesArray = [[NSMutableArray alloc] init];
for (int images = 0; images < 15; images++) {
UIImage* buttonImage = [UIImage imageNamed:[NSString stringWithFormat:#"aaqqq00%02d.png", images]];
[imagesArray addObject:buttonImage];
}
NSArray* reversedAnim = [[imagesArray reverseObjectEnumerator] allObjects];
int buttonTag = button.tag;
for (int images = 1; images <= 10; images++) {
UIButton *animButton = (UIButton *)[self.view viewWithTag:images];
if (images <= buttonTag) {
animButton.imageView.animationImages = imagesArray;
[animButton setImage:
[UIImage imageNamed:#"aaqqq0014.png"] forState:UIControlStateNormal];
animButton.adjustsImageWhenHighlighted = NO;
animButton.imageView.animationDuration = 1; //whatever you want (in seconds)
animButton.imageView.animationRepeatCount = 1;
[animButton.imageView startAnimating];
} else {
if (currentButtonTag_ >= images) {
animButton.imageView.animationImages = reversedAnim;
[animButton setImage:
[UIImage imageNamed:#"aaqqq0000.png"] forState:UIControlStateNormal];
animButton.adjustsImageWhenHighlighted = NO;
animButton.imageView.animationDuration = 0.2; //whatever you want (in seconds)
animButton.imageView.animationRepeatCount = 1;
[animButton.imageView startAnimating];
}
}
}
As you iterate through your loop, have a variable that holds the desired delay, and add to it at the end of each loop. We'll call this delay.
Now, instead of calling:
[animButton.imageView startAnimating];
Do this:
[animButton.imageView performSelector:#selector(startAnimating) withObject:nil afterDelay:delay];
And each button will start animating after the accumulated delay.
You need to give some delay time and duration time.
[UIView animateWithDuration:0.6
delay:2.0
options: UIViewAnimationCurveEaseOut
animations:^{S1Button.frame = CGRectMake(20, 10, 50, 10);S1buttonA.alpha = 0;}
completion:nil];
Set this for every animation.

Resources