Simple NSTimer progress bar for 7 seconds - ios

In theory this progress bar should last 7 seconds, however it seems to run a little long. My math has to be incorrect or I'm overlooking something.
Timer should be firing 100 times in 1 second and the progress bar should take about 7 times longer than that to reach 1.0
Any help would be greatly appreciated.
- (void)startTimer;
{
[pBar showProgress:self.progress];
[self.timer invalidate];
self.timer = nil;
if (self.progress < 1.0) {
CGFloat step = 0.01;
self.timer = [NSTimer scheduledTimerWithTimeInterval:step target:self
selector:#selector(startTimer)
userInfo:nil repeats:NO];
self.progress = self.progress + 0.00143;
} else {
[self performSelector:#selector(stopProgress)
withObject:nil afterDelay:0.5];
}
}

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

How to change color of a UIButton while pressed ONLY and cancel color change if released before x amount of seconds?

I'm trying to make a button change color while being pressed for 3 seconds. Once timer reaches 3rd second the color change is permanent but if user releases button before time is up the button goes back to its original color. What I have so far is this:
in viewDidLoad
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]
initWithTarget:self
action:#selector(fireSomeMethod)];
longPress.minimumPressDuration = 3.0;
[self.view addGestureRecognizer:longPress];
in fireSomeMethod I have
- (void)someMethod {
[UIView transitionWithView:self.button
duration:2.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
self.button.backgroundColor = [UIColor redColor];
}
completion:^(BOOL finished) {
NSLog(#"animation finished");
}];
}
This requires me to hold the button down for 3 seconds for the animation to fire and animation itself takes 2 seconds to complete. The desired behavior is that animation starts when longPress starts and I release button before 3 seconds everything goes back to the way it was. Thanks for your help in advance
Make use of button event no need to use UILongPressGestureRecognizer
Make 2 action for your button, one is for Touch Down and other is for Touch Up Inside
like this
// For `Touch Up Inside`
- (IBAction)btnReleased:(id)sender {
[timer invalidate];
NSLog(#"time - %d",timeStarted);
}
// For `Touch Down`
- (IBAction)btnTouchedDown:(id)sender {
timer = [NSTimer scheduledTimerWithTimeInterval:1.0f
target:self
selector:#selector(_timerFired:)
userInfo:nil
repeats:YES];
timeStarted = 0;
}
- (void)_timerFired:(NSTimer *)timer {\
timeStarted++;
}
Create 2 variable timer of type NSTimer and timeStarted of type int. Fire the timer on Touch Down and invalidate it on Touch Up Inside, and in Touch Up Inside action method get the total time till your button is hold.As shown in the above code

UILabel updating incorrectly with NSTimer

I am showing a count down timer using UILabel and NSTimer -
-(void)a_Method
{
[coolTimeLbl setNeedsDisplay];
coolTime = 5; // it is an int
coolingTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(cooling) userInfo:nil repeats:YES]; // NSTimer
}
-(void)cooling
{
if (coolTime>0)
{
coolTime = coolTime-1;
NSLog(#" coolTime----%#",coolTime);
coolTimeLbl.text =[NSString stringWithFormat:#"%d",coolTime];
NSLog(#" coolTimeLbl----%#",coolTimeLbl.text);
}
else
{
[coolingTimer invalidate];
coolingTimer = nil;
}
}
The first time everything works fine and I am getting coolTimeLbl.text as - 4 3 2 1 0
But the second time when I call aMethod, coolTimeLbl is not getting updated properly - it is like 3 2 0 etc (some weird behavior)
However both NSLogs (coolTime & coolTimeLbl) print perfectly all the times and values.
Why does this happen? I tried many ways like NSNotification etc.
Please help me to fix this.
If you're calling a_Method more than once before coolingTimer invalidates itself, the timer will tick more than once.
You should add some boolean like ;
BOOL isTimerValid;
in a_Method,
if(!isTimerValid)
{
isTimerValid = YES;
coolingTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(cooling) userInfo:nil repeats:YES]; // NSTimer
}
in cooling,
else
{
isTimerValid = NO;
....
}
Had the same issue in one of my viewControllers and another one was working OK with same NSTimer code. Looked at about 20 SO threads to get it solved. No luck. In my case
myLabel.opaque = false
solved it.
Don't ask me why.

When a button passes through a specified Y value, all the other buttons falling also disappear with it

I'm trying to make multiple buttons fall down the screen all at once, at different speeds. But when I have my if statement check if it passed through the value, all the other buttons disappear with it. I'm also incrementing the score to go -1 each time a button passes through the Y value. Any help is appreciated, thanks.
- (void)b1Fall
{
b1.center = CGPointMake(b1.center.x, b1.center.y+6);
if (b1.center.y >= 506) {
[self updateScore];
b1.center = CGPointMake(44, 11);
}
}
- (void)b2Fall
{
b2.center = CGPointMake(b2.center.x, b2.center.y+7);
if (b2.center.y >= 506) {
[self updateScore];
b2.center = CGPointMake(160, 11);
}
}
- (void)b3Fall
{
b3.center = CGPointMake(b3.center.x, b3.center.y+8);
if (b3.center.y >= 506) {
[self updateScore];
b3.center = CGPointMake(276, 11);
}
}
- (void)updateScore
{
healthLabel.text = [NSString stringWithFormat:#"%d", [healthLabel.text intValue]-1];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// REMOVE AFTER TESTING
b1Timer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:#selector(b1Fall) userInfo:nil repeats:true];
b2Timer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:#selector(b2Fall) userInfo:nil repeats:true];
b2Timer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:#selector(b3Fall) userInfo:nil repeats:true];
}
A few things:
1.) Put these animations in viewDidAppear instead of viewDidLoad - you want the animation to begin when the user is actually looking at the buttons, not when the view is loaded into memory, where the user may not be able to see it (viewDidLoad).
2.) Don't use NSTimer for animations, use this:
[UIView animateWithDuration:0.5
delay:1.0
options: UIViewAnimationCurveEaseOut
animations:^{
}
completion:^(BOOL finished){
// loop your animation here.
}];
3.) If you want to make a game, I don't recommend using this approach. All animations using UIKit are going to perform on the main thread and block the user flow. What you want is a framework like Cocos2D where the animations are doing in the GPU and ASYNC game logic is readily supported. UIKit isn't a good choice for game development in general.

NSTimer not firing when it should

I have a table view that is broken up into alphabetic sections. I am displaying an animated banner in a UIImage View in the footer of each section, and need to determine which image is displayed when the UIImage View is clicked. I set a timer right before I make the call to startAnimating. The timer is to fire every 5 seconds, at the same rate that the animation changes, but the timer is firing much faster. Sometimes it will fire 2 or 3 times within that 5 second period. Here is the code where I start the timer and the animation:
-(UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger) section {
...
imgAdBar = [[bannerView alloc]initWithFrame:CGRectMake(footer.frame.origin.x,footer.frame.origin.y,footer.frame.size.width,footer.frame.size.height)];
imgAdBar.image=[UIImage imageNamed:[NSString stringWithFormat:#"%#", [animationArray objectAtIndex:0]]];
[imgAdBar saveBannerArray:animationArray];
[imgAdBar setUserInteractionEnabled:YES];
imgAdBar.animationImages = images;
imgAdBar.animationDuration=[images count]*5;
imgAdBar.animationRepeatCount=0;
timerCount=0;
[NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(timerRunning:) userInfo:nil repeats:YES];
[imgAdBar startAnimating];
[footer addSubview:imgAdBar];
footer.backgroundColor = [UIColor clearColor];
}
return footer;
}
And here is the selector:
-(void)timerRunning:(NSTimer*)theTimer
{
NSLog(#"timerCount=%d",timerCount);
imgAdBar.timerCount=timerCount;
if (timerCount==numAnimationImages) {
timerCount=0;
}
NSLog(#"currentImage=%#",[animationArray objectAtIndex:timerCount]);
timerCount++;
}
I plan to use that timer to index into my image array so that I can tell which is displayed. Anyone have any idea why it's not firing when it should? Thanks for any assistance!
You have to use NSTimer as an property ...
Since viewForFooterInSection is called several times for many sections you have to invalidate before you reinitialize it or you have to check for null following is the code..
Invalidate:
NSTimer *timer; // declare in ViewDidLoad
// code in viewForFooterInSection
[timer invalidate];
timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
target: self
selector: #selector(handleTimer:)
userInfo: nil
repeats: YES];
Check for nil
if (timer == nil) {
timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
target: self
selector: #selector(handleTimer:)
userInfo: nil
repeats: YES];
}
Hope it should help..
Declare an NSTimer as property in your header file
#propterty (nonatomic, retain) NSTimer *someTimer;
In the line where you fire the timer
someTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(timerRunning:) userInfo:nil repeats:YES];
Don't forget to release it in -(void)viewDidUnload
[someTimer release];
Since you do not use the value of timerCount unless there is a click, you do not need to update it on timer: it is sufficient to store the time at the moment when you start animation. Knowing that each image is shown for five seconds, you can calculate the index of the image being displayed by taking the difference, in seconds, between the time of the click and the time when you started the animation, dividing it by five, and taking the remainder of the division by the total number of images.
Say you have ten images, each displaying for five seconds in a loop. Let's also say that the animation has started at 08:15:51. Now let's say there's a click at 08:19:23, or 212 seconds after the animation has started. After dividing by five you get 42; taking the remainder of the division by ten, you get 2. Therefore, you know that the user has clicked on the third image in the animation loop (as usual, indexes start from zero).

Show/hide a button after a period of time

I am currently making an app that has a complex loading screen. I have created the loader using UI Animation, but want to add a button that will appear once the loading bar has finished. I have come across the idea of hiding the button for a certain period of time, or making it appear after a certain period of time.
How would I show/hide the button after a period of time?
You could invoke your method to show the button after a certain period of time:
[self performSelector:#selector(showButton) withObject:nil afterDelay:0.5];
Or, probably better, if you want to animate the appearance of the button, you can do both the animation and the delay in a single call, e.g. assuming the button originally has alpha of 0.0:
[UIView animateWithDuration:0.25
delay:0.5
options:nil
animations:^{
myButton.alpha = 1.0;
}
completion:^(BOOL finished){
// if you want to do anything when animation is done, do it here
}
];
Using NSTimer should be the easiest way.
Create NSTimer to do that,
Timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(hideButton:) userInfo:nil repeats:NO];
-(void)hideButton:(UIButton *)hideButton {
if hideButton.isHidden == false {
hideButton.hidden=TRUE;
} else {
hideButton.hidden = FALSE
}

Resources