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 t开发者_如何学JAVAime 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.
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:
- It is a standard element provided by apple.
- This option doesn't have the overhead of creating and loading 100 images (as suggested by Krypton in the comments).
- 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
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.
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];
}
精彩评论