iPhone achieve smooth animation using progress bar

2020-04-08 12:46发布

I am trying to implement a simple quiz app on the iphone in the spirit of learning, part of this app is a timer.

I want my timer to count down from 10 to 0. I had a simple NSTimer that repeats and calls a method every second and in this method I update a label which displays the remaining time, which works nicely.

Now instead of displaying the timer using a label I wanted to use a graphical progress bar, so I created ten images, one full in length (representing 10), the next 9/10 of the size and so on, and in my repeating timer method instead of updating a label I update a UIImage with the appropriate image so over time the progress bar gets smaller and smaller.

My problem is that due to the way I have implemented the progress bar, it doesn't look very smooth when it updates every second. Is there another way I should approach developing this kind of functionality? I have heard that you could use a stretchable image to get a smoother effect but I couldn't see any good examples of that.

Any advice and code samples welcome, just trying to learn here.

3条回答
一夜七次
2楼-- · 2020-04-08 13:20

After investigating a few options I have decided to use a UIProgressView (suggested by sudo in comments) coupled with a NSTimer which updates the progress ten times a second for ten seconds, I used this solution because:

  1. It is a standard element provided by apple.
  2. This option doesn't have the overhead of creating and loading 100 images (as suggested by Krypton in the comments).
  3. By the NSTimer I can add a check to the function updateProgressBar to add some feedback to the user when the timer is reaching low, e.e. vibrate for the last three seconds etc (using animation I don't think I would have that option).

Using the code below assume I have a UIProgressView variable named 'progressView', an NSTimer named 'timer' and a floating point variable named 'time', then:

-(void)updateProgressBar
{
    if(time <= 0.0f)
    {
        //Invalidate timer when time reaches 0
        [timer invalidate];
    }
    else
    {
        time -= 0.01;
        progressView.progress = time;
    }
}

-(void)animateProgressView2
{
    progressView.progress = 1;
    time = 1;
    timer = [NSTimer scheduledTimerWithTimeInterval: 0.1f
                                             target: self
                                           selector: @selector(updateProgressBar)
                                           userInfo: nil
                                            repeats: YES];
}

Another way I investigated (after seeing Disco's comments) but decided against was to use animation. In this instance I created two png images, named 'redbar.png' and 'greenbar.png', the idea bring they would be placed over each other (the green bar being on top/in the foreground), and over the duration of the animation the green bar shrinks, revealing the red bar in the background.

UIImage *green= [UIImage imageNamed:@"greenbar.png"];
UIImage *red= [UIImage imageNamed:@"redbar.png"];

redBar.image = red; // allocate the red image to the UIImageView
redBar.frame = CGRectMake(20,250,220,30);// set the size of the red image

greenBar.image = green; // allocate the green image to the UIImageView
greenBar.frame = CGRectMake(20,250,220,30);//set the size of the green image

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:10]; //the time you want the animation to last for
[UIView setAnimationDidStopSelector:@selector(animationFinished)];//the method to be called once the animation is finished
    //at the end of the animation we want the greenBar frame to disappear
    greenBar.frame = CGRectMake(20,250,0,30);
[UIView commitAnimations];

An ideal solution would be to combine the UIProgressView and the UIAnimation block, but this is not currently an option (see this question animate a UIProgressView's change).

Hope this helps someone in the future.

EDIT::

It appears the ideal solution I mention above is now available in iOS5, taken from the apple reference site:

setProgress:animated: Adjusts the current progress shown by the receiver, optionally animating the change.

  • (void)setProgress:(float)progress animated:(BOOL)animated
查看更多
Anthone
3楼-- · 2020-04-08 13:29

May not be what you're looking for but may help you. Why not have a UIImageView with your image at full size then apply some UIView animations to it over a set duration? This way the shrinking will be smooth.

UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"YourImage.png"]];
imageView.frame = CGRectMake(0,0,320,30);//starts big
[self.view addSubview:imageView];
[UIView beginAnimations:@"Progress Bar Animation" Context:nil];
[UIView setAnimationDuration:10]; //whatever time you want
imageView.frame = CGRectMake(0,0,10,30);//ends small
[UIView commitAnimations];

This may be what you're looking for, I'm not sure. Just seems a bit easier than using ten different images.

查看更多
时光不老,我们不散
4楼-- · 2020-04-08 13:37

I did by increasing the setting progress from (0.01 - 1.00) to (0.001 - 1.000) and decrease the time difference between setting progress values.

Here is my below code with three different speeds of animation.

 -(void)setProgressViewProgress
{
    //Check for completion
    if(progress > 1.0)
    {
        [self progressCompleted];
        return;
    }
    //Diffrent speed for range
    CGFloat speed;
    //From 0.000 to 0.500 speed is 500*0.007 = 3.5 seconds    0.007(sec) => x(sec) = Total time in range(sec) / 500.0 ==> 3.5/500.0 = 0.07
    if(progress<0.50)
    {
        speed = 0.007;
    }
    //From 0.500 to 0.750 speed is 250*0.005 = 1.25 seconds 0.005(sec) => x(sec) = Total time in range(sec) / 500.0 ==> 1.25/250.0 = 0.05
    else if(progress<0.75)
    {
        speed = 0.005;
    }
    //From 0.750 to 1.0 speed is 250*0.002 = 0.5 seconds 0.002(sec) => x(sec) = Total time in range(sec) / 500.0 ==> 0.5/250.0 = 0.02
    else
    {
        speed = 0.002;
    }
    //Increase the progress view by 1/1000  for smooth animation
    progress+=0.001;
    [progressView setProgress:progress];
    [self performSelector:@selector(setProgressViewProgress) withObject:nil afterDelay:speed];
}
查看更多
登录 后发表回答