1
votes

I am a little confused about something I was just trying for fun. I have written a little method called animateBars_V1 which uses an array of UIImageViews and alters the height of each UIImageView to show a changing set of coloured bars.

enter image description here

- (void)animateBars_V1 {
    srandom(time(NULL));
    for(UIImageView *eachImageView in [self barArray]) {
        NSUInteger randomAmount = kBarHeightDefault + (random() % 100);

        CGRect newRect;
        CGRect barRect = [eachImageView frame];

        newRect.size.height = randomAmount;
        newRect.size.width = barRect.size.width;
        newRect.origin.x = barRect.origin.x;
        newRect.origin.y = barRect.origin.y - (randomAmount - barRect.size.height);
        [eachImageView setFrame:newRect];
    }
}

This works fine, I then added a UIButton with a UIAction for when the button is pressed. Each time the button is pressed animateBars_V1 is called and the coloured bars update.

- (IBAction)buttonPressed {
    for(int counter = 0; counter<5; counter++) {
        [self animateBars_V1];
        NSLog(@"COUNTER: %d", counter);
    }
}

My question is just for fun I decided that each time the button is pressed I would call animateBars_V1 5 times. What happens is that the bars don't change until after the loop has exited. This results in:

Screen as per storyboard
COUNTER: 0
COUNTER: 1
COUNTER: 2
COUNTER: 3
COUNTER: 4
Screen Updates

Is this the correct behaviour? I don't need a fix or workaround as this was just for fun, I was more curious what was happening for future reference.

3

3 Answers

4
votes

If you are calling animateBars_V1 multiple times within a loop, the frames of the bars do get set multiple times, but before they can be rendered, animateBars_V1 gets called again, and the frames are set to a new position/size.

The call to render (drawRect: and related methods) doesn't occur until after the loop is finished - since it is an IBAction, it is by necessity called in the main thread, which means that all rendering is blocked until the code is completed.

There are of course several solutions to this. A simple method to do the multi-animation thing is to use UIView animateWithDuration:animations:completion: in the following manner:

- (IBAction)buttonPressed {
    [self animateBarsWithCount:5];
}

- (void)animateBarsWithCount:(int)count
{
    [UIView animateWithDuration:.25f animations:^{
        [self animateBars_V1];
    }completion:^(BOOL finished){
        [self animateBarsWithCount:count - 1];
    }];
}

//animateBars_V1 not repeated

Of course, if you simply wanted to run the animation one time, (but actually animated) you should do it like this:

- (IBAction)buttonPressed {
    [UIView animateWithDuration:.25f animations:^{
        [self animateBars_V1];
    } completion:nil];
}
2
votes

CrimsonDiego is right

you can try to delay each call with this:

- (IBAction)buttonPressed {
    for(int counter = 0; counter<5; counter++) {
        float ii = 1.0 * counter / 10;
        [self performSelector:@selector(animateBars_V1) withObject:nil afterDelay:ii];
        //  [self animateBars_V1];
        NSLog(@"COUNTER: %d", counter);
    }
}
0
votes

The problem is here

for(int counter = 0; counter<5; counter++) {
    [self animateBars_V1];
    NSLog(@"COUNTER: %d", counter);
}

This for loop is executed in nano seconds and your eye is not able to catch up that change as eye can detect only 1/16th of a second. For testing what can run this code in a timer that runs five time.

Edited Removed sleep call as it will sleep the main thread and everything will stop. So use Timer here