Swift Progress View with Nstimer

Swift Progress View with NSTimer

OK - so if you want the progress bar to update every second, then you need a timer that fires every second - but which does it 20 times, and calls setProgressBar as selector instead of getNextPoseData

within setProgressBar, you need to increment a class-level attribute, indexProgressBar perhaps, and simply set the progress bar attribute progress to 1.0 / indexProgressBar

if indexProgressBar == 20, then call getNextPoseData, and reset your progress bar

and here's a simplified version of how you might do that

class ViewController: UIViewController
{
@IBOutlet weak var progressBar: UIProgressView!
var timer = NSTimer!()
var poseDuration = 20
var indexProgressBar = 0
var currentPoseIndex = 0

override func viewDidLoad()
{
super.viewDidLoad()

// initialise the display
progressBar.progress = 0.0
}

@IBAction func cmdGo(sender: AnyObject)
{
// display the first pose
getNextPoseData()

// start the timer
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "setProgressBar", userInfo: nil, repeats: true)
}

func getNextPoseData()
{
// do next pose stuff
currentPoseIndex += 1
print(currentPoseIndex)
}

func setProgressBar()
{
if indexProgressBar == poseDuration
{
getNextPoseData()

// reset the progress counter
indexProgressBar = 0
}

// update the display
// use poseDuration - 1 so that you display 20 steps of the the progress bar, from 0...19
progressBar.progress = Float(indexProgressBar) / Float(poseDuration - 1)

// increment the counter
indexProgressBar += 1
}
}

Swift - UIProgressView is not smooth with NSTimer

Edit: A simple 3 second UIView animation (Recommended)

If your bar is just moving smoothly to indicate activity, possibly consider using a UIActivityIndicatorView or a custom UIView animation:

override func viewDidAppear(animated: Bool)
{
super.viewDidAppear(animated)

UIView.animateWithDuration(3, animations: { () -> Void in
self.progressView.setProgress(1.0, animated: true)
})
}

Make sure your progressView's progress is set to zero to begin with. This will result in a smooth 3 second animation of the progress.

Simple animated progress (Works but still jumps a bit)

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIProgressView_Class/#//apple_ref/occ/instm/UIProgressView/setProgress:animated:

func setProgress() {
time += 0.1
progressView.setProgress(time / 3, animated: true)
if time >= 3 {
timer!.invalidate()
}
}

Option with smaller intervals. (Not recommended)

Set your timer to a smaller interval:

timer = NSTimer.scheduledTimerWithTimeInterval(0.001, target: self, selector:Selector("setProgress"), userInfo: nil, repeats: true)

Then update your function

func setProgress() {
time += 0.001
progressView.setProgress(time / 3, animated: true)
if time >= 3 {
timer!.invalidate()
}
}

how can i update my progress bar step by step?

done with DispatchQueue.global(priority: .default).async

func upgradeArray() {
for i in 0...10 {
DispatchQueue.global(priority: .default).async {
//sleep(1)
self.array.append(i)
print(i)
DispatchQueue.main.async(execute: {
let percentProgress = Float(Float(self.array.count)*100.0/10.0)
self.progressBar.setProgress(percentProgress, animated: true)
})
}
}

}

Why I can't stop Progress Bar with Timer?

I guess you are creating multiple timer scheduled by every clicked. You can try this. First, check the timer code as below:

if self.timer.isValid == true {
self.timer.invalidate()
}
timer = Timer.scheduledTimer(timeInterval: 0.6, target: self, selector: #selector(updateProgress), userInfo: nil, repeats: true)

Now, inside your updateProgress, update like this.

@objc func updateProgress() {
if activePlayer == 1 {
progressBar.progress -= 0.01

} else {
progressBar.progress += 0.01
}

if progressBar.progress == 1.0 || progressBar.progress == 0.0 {
if activePlayer == 1 {
gameOverLabel.text = "Noughts has won!"

} else {
gameOverLabel.text = "Crosses has won!"
}
endGame()
}
}

How to set progress bar With timer after view controller loaded

The first timer event will be fired after 1 second is passed, so you should see 29 first. So you should display the number 30 manually somehow, outside the timer event.

And you subtract 1 in your code in setHintTextGenerationBlock :
progress = timeRemain - 1;
which will make you see 28 as initial.

And as final you should start timer in viewDidAppear instead of viewWillAppear where you lose another second and come to 27.

To correct code should be like:

@interface TestViewController ()

{
NSTimeInterval totalCountdownInterval;
NSDate* startDate;
}
}

-(void)viewDidLoad {
[_circleProgressBar setHintTextGenerationBlock:(^NSString *(CGFloat progress) {

return [NSString stringWithFormat:@"%.0f", progress * 30];

})];
[_circleProgressBar setProgress:1.0) animated:NO];
}

-(void)viewDidAppear:(BOOL)animated{

[self startTimer];
totalCountdownInterval = 30.0;
startDate = [NSDate date];
}

-(void)startTimer {

if (!_timer) {
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0f
target:self
selector:@selector(timerFired:)
userInfo:nil
repeats:YES];
}
}

-(void)stopTimer {

if ([_timer isValid]) {
[_timer invalidate];
}
_timer = nil;
}

-(void)timerFired:(NSTimer *)timer {

NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSinceDate:startDate];
NSTimeInterval remainingTime = totalCountdownInterval - elapsedTime;

CGFloat timeRemain = remainingTime;
NSLog(@"%f", timeRemain);
if (remainingTime < 0.0) {
[_timer invalidate];
}

[_circleProgressBar setProgress:(1.0 * timeRemain / 30.0) animated:YES];
}

-(void) viewWillDisappear:(BOOL)animated {
[self stopTimer];
}

You should set progress to 1.0 in the beginning and count down to 0.0 in 30 steps.
Set hint generation block to display something meaningful for 1.0 - 0.0 as 30 - 0.

Make UIProgressView as a count down timer

You're attempting to update the progress view 1000 times in 1.5 seconds. That's way too fast, since the screen only updates 60 times per second. In other words, you're updating the progress bar more than 10 times between each time that the progress bar is actually redrawn on the screen.

Instead I would suggest 15 updates at 0.1 second intervals, and change the progress bar by 1/15 each time.

One way to check how well the code is performing is to use the CACurrentMediaTime function to get timestamps. Here's some sample code that demonstrates how to do that. The progressStart variable is the timestamp when the button press event occurred, and the NSLog prints the amount of time elapsed relative to the start time.

An important feature of the code is that the performSelector method is called as early as possible in the updateProgress method, to minimize slippage.

@interface ViewController ()
{
CFTimeInterval progressStart;
int progressCount;
}
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
@end

- (void)updateProgress
{
if ( progressCount > 0 )
[self performSelector:@selector(updateProgress) withObject:nil afterDelay:0.1];

self.progressView.progress = progressCount / 15.0;
NSLog( @"%2d %.3lf", progressCount, CACurrentMediaTime() - progressStart );
progressCount--;
}

- (IBAction)someButtonPressed
{
self.progressView.progress = 1.0;
progressStart = CACurrentMediaTime();
progressCount = 15;
[self updateProgress];
}

And here are the results from a typical run

2015-07-01 13:05:57.610 Progress[8354:907] 15 0.000
2015-07-01 13:05:57.711 Progress[8354:907] 14 0.101
2015-07-01 13:05:57.813 Progress[8354:907] 13 0.203
2015-07-01 13:05:57.914 Progress[8354:907] 12 0.304
2015-07-01 13:05:58.015 Progress[8354:907] 11 0.405
2015-07-01 13:05:58.116 Progress[8354:907] 10 0.506
2015-07-01 13:05:58.218 Progress[8354:907] 9 0.608
2015-07-01 13:05:58.319 Progress[8354:907] 8 0.709
2015-07-01 13:05:58.420 Progress[8354:907] 7 0.810
2015-07-01 13:05:58.520 Progress[8354:907] 6 0.910
2015-07-01 13:05:58.621 Progress[8354:907] 5 1.011
2015-07-01 13:05:58.722 Progress[8354:907] 4 1.112
2015-07-01 13:05:58.823 Progress[8354:907] 3 1.213
2015-07-01 13:05:58.924 Progress[8354:907] 2 1.314
2015-07-01 13:05:59.024 Progress[8354:907] 1 1.415
2015-07-01 13:05:59.125 Progress[8354:907] 0 1.515

Note that the performSelector:afterDelay method has about 1 millisecond of slippage on each event. The total slippage was 15 milliseconds. The device screen update rate is 60 frames/sec, which is 16.7 msec/frame. So the total slippage was less than one frame time, and won't be noticeable to the user.

As rmaddy pointed out in the comments, using an NSTimer allows you to avoid most of the slippage. However, the last timer event could still slip by an arbitrary amount of time.

working with UIProgressView to set progress bar to 30 minutes

I think it's only a math problem.

First, myProgressView.progress can assume values from 0.0 to 1.0.

As you call moreProgress each second, you'll need to call it 1800 times for 30 minutes. Thus, myProgressView.progress should increase 1/1800 by second.

Change your code to:

- (void) moreProgress {
myProgressView.progress += 1.0/1800.0;
}

Hope it helps you! :)

Simple NSTimer progress bar for 7 seconds

You should not update as often as you do. 100 times per second is way too often. 60 would be sufficient to achieve a good frame-rate in theory. However a UIProgressBar can update its value with an animation. Hence you only need to update say 70 times in total or 10 times per second and make the changes with an animation.

I would go for 10 animated updates or less. Or you could try to update with one animation:

[UIView animateWithDuration:7.0 animations:^{
[progressView setProgress:1.0 animated:YES]; //maybe try animated: NO here
} completion:^(BOOL finished) {
//ended
}];

While I did not test this approach, it seems far cleaner than manually performing what essentially is a timed animation of the progress.



Related Topics



Leave a reply



Submit