I want to create a button that takes me to another view controller and automatically starts a countdown from 3 to 0, but i don't know how to set the countdown on the other view controller. Here's the code i tried:
#implementation TestViewController
-(IBAction)test:(id)sender {
CountdownViewController *cdvc = [[CountdownViewController alloc]
initWithNibName:#"CountViewController" bundle:nil];
[self.navigationController pushViewController:cdvc animated:YES];
}
#implementation CountdownViewController
int maintInt = 3;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countDown) userInfo:nil repeats:YES];
-(void)countDown {
maintInt -= 1;
count.text = [NSString stringWithFormat:#"%i", maintInt];
if(maintInt==1){
[timer invalidate];
}
}
You have to place your countdown code in a method, which you should call in viewDidLoad or viewWillAppear method regarding your needs.
#implementation TestViewController
-(IBAction)test:(id)sender {
CountdownViewController *cdvc = [[CountdownViewController alloc]
initWithNibName:#"CountViewController" bundle:nil];
[self.navigationController pushViewController:cdvc animated:YES];
}
#end
#implementation CountdownViewController
- (void)viewDidLoad {
[super viewDidLoad];
// .. customize your views here ..
// Initialize the timer once your view has been loaded.
//
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countDown:) userInfo:nil repeats:YES];
}
static int maintInt = 3;
- (void)countDown:(NSTimer *)timer {
count.text = [NSString stringWithFormat:#"%i", maintInt];
maintInt -= 1;
if (maintInt == 0 && timer) {
[timer invalidate];
timer = nil;
}
}
#end
Related
I have a timer in a UIViewController:
#interface MyViewController : UIViewController <UITableViewDataSource,UITableViewDelegate, MFMailComposeViewControllerDelegate>
...
#property (nonatomic, assign) int m_remaining_time;
...
#end
In .m file:
#interface MyViewController ()
{
NSTimer *m_timer;
}
The timer starts on viewDidAppear()
m_timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(decrementSpin) userInfo:nil repeats:YES];
The method decrementSpin simply decrements the counter and prints a log:
- (void)decrementSpin
{
self.m_remaining_time--;
if(self.m_remaining_time > 0) {
NSLog(#"[TIMER] >> %d", self.m_remaining_time);
}
else {
NSLog(#"[TIMER] >> Time finished!");
[m_timer invalidate];
m_timer = nil;
}
}
I destroy the timer in every case the ViewController will close:
In appDidEnterInBackground, viewDidDisappear, when a button that closes the view has been pushed (goind to next view for ex)
The problem is that, in some cases, I have seen in the simulator the log [TIMER] >> %d that keeps printing even after I closed the view.
It is not always the case, but sometimes it keeps counting.
Any clues ?
Edit:
#skaak, I do the following code in appDidEnterInBackground, viewDidDisappear:
if(m_timer) {
[m_timer invalidate];
m_timer = nil;
}
Then when the player pushes the "Next" and "Home" buttons.
For example the case of "Home" button:
-(IBAction)homeButtonPressed:(id)sender{
if(m_timer) {
[m_timer invalidate];
m_timer = nil;
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
InitScreenViewController* initVc = [self.storyboard instantiateViewControllerWithIdentifier:#"initScreenViewController"];
UINavigationController *navVC = [[UINavigationController alloc] initWithRootViewController:initVc];
[navVC setNavigationBarHidden:true];
[UIView transitionWithView:[AppDelegate getShareInstance].window duration:0.1f options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
[AppDelegate getShareInstance].window.rootViewController = navVC;
}completion:nil];
}
Edit #2:
I added the following flag:
#property (assign, nonatomic) BOOL isCurrentlyViewShowed;
Which is set on viewDidAppear (along with your suggestion):
self.isCurrentlyViewShowed = TRUE;
if(m_timer) {
[m_timer invalidate];
m_timer = nil;
}
m_timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(decrementSpin) userInfo:nil repeats:YES];
Then, it is set to off everytime the view is closed:
if(m_timer) {
[m_timer invalidate];
m_timer = nil;
}
self.isCurrentlyViewShowed = FALSE;
And finally, on decrementSpin:
if(self.isCurrentlyViewShowed == FALSE){
if(m_timer) {
[m_timer invalidate];
m_timer = nil;
}
}
self.m_remaining_time--;
if(self.m_remaining_time > 0) {
NSLog(#"[TIMER] (%p)>> %d (FLAG: %d)", m_timer, self.m_remaining_time, self.isCurrentlyViewShowed);
}
else {
NSLog(#"[TIMER] (%p)>> Time finished (FLAG: %d)", m_timer, self.isCurrentlyViewShowes);
[m_timer invalidate];
m_timer = nil;
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
}
I did repeated the test like 100 times and I managed to reproduce it once!
I left this view, and I saw on the log the line:
[TIMER] (0x2818ecfc0)>> 58 (FLAG: 1)
[TIMER] (0x2818ecfc0)>> 57 (FLAG: 1)
...
It means that somehow the View object is re-initialized ? I don't get it..
This is tricky, and need the whole code to try and isolate it. But here is an idea that might help. In fact, I think you need to do it like this anyhow. Change your viewDidAppear to something like below.
- viewDidAppear
{
if ( ! m_timer )
{
m_timer = ...
}
}
or, depending on the logic, to
- viewDidAppear
{
if ( m_timer )
{
[m_timer invalidate];
m_timer = nil;
}
m_timer = ...
}
In either case you will be sure you have only one timer running at a time which may be your problem.
I create a subclass of UILabel called NewLabel, in NewLabel.m, codes like this
+ (NewLabel*)addLabelIntoView:(UIView*)view
{
NewLabel *label = [[NewLabel alloc] init];
//some codes
[view addSubview:label];
[NSTimer scheduledTimerWithTimeInterval:1.0f target:view selector:#selector(callActionInVC) userInfo:nil repeats:NO];
return label;
}
and there's a UIViewController, codes in UIViewController.m
- (void)viewDidLoad {
[NewLabel addLabelIntoView:self.view]
}
- (void)callActionInVC {
//do some actions
}
I want to call the function callActionInVC of UIViewController.m in NewLabel.m, and in NewLabel.m, target:view is not right, What should I change to? What's the target of a UIViewController in a subclass?
Or is there any ideas to make this demand?
the target is wrong
[NSTimer scheduledTimerWithTimeInterval:1.0f target:view selector:#selector(callActionInVC) userInfo:nil repeats:NO];
You should set target as the controller.
[NSTimer scheduledTimerWithTimeInterval:1.0f target:<your controller> selector:#selector(callActionInVC) userInfo:nil repeats:NO];
You may need to create a category to get the controller.
- (UIViewController *)fy_parentController{
UIResponder *responder = [self nextResponder];
while (responder) {
if ([responder isKindOfClass:[UIViewController class]]) {
return (UIViewController *)responder;
}
responder = [responder nextResponder];
}
return nil;
}
The final code looks like this:
[NSTimer scheduledTimerWithTimeInterval:1.0f target:view.fy_parentController selector:#selector(callActionInVC) userInfo:nil repeats:NO];
Got an issue with my countdown timer who don't want to countdown from 60 and reset when 0 is reached. At the moment it just sets its label 0 and start counting down to -1, 2-... How do I get it to start from 60 on iOS in xcode?
.m file
#import "ViewController.h"
#interface ViewController ()
{
int timeTick;
NSTimer *timer;
}
#end
#implementation ViewController
- (IBAction)stopStartBtn:(id)sender {
[timer invalidate];
timeTick = 3;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(myTicker) userInfo:nil repeats:YES];
}
-(void)myTicker{
timeTick--;
NSString *timeString =[[NSString alloc] initWithFormat:#"%d", timeTick];
self.display.text = timeString;
if(timer >= 0){
[timer invalidate];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
timeTick = 3;
}
#end
.h File
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet UILabel *display;
- (IBAction)stopStartBtn:(id)sender;
#end
Your code currently starts the timer at 0, decrements it, and then checks it it has reached 60. Obviously that won't happen.
If you want to start at 60 and stop at 0 then you need to set timeTick to 60 in viewDidLoad and check for a value of 0 in myTicker.
And don't forget to call [super viewDidLoad]; in your viewDidLoad method.
You also need to fix your check to see if you've reached zero. Right now you are looking at the timer pointer instead of timeTick integer.
Change:
if(timer >= 0){
[timer invalidate];
}
to:
if (timeTick <= 0) {
[timer invalidate];
}
You are also never setting your timer instance variable. You actually set a local variable of the same name.
Change:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(myTicker) userInfo:nil repeats:YES];
to:
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(myTicker) userInfo:nil repeats:YES];
#interface Ovning1 ()
{
int timeTick;
NSTimer *timer;
}
#end
#implementation Ovning1
- (IBAction)stopStartBtn:(id)sender {
[timer invalidate];
timeTick = 60;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(myTicker) userInfo:nil repeats:YES];
}
-(void)myTicker{
timeTick--;
NSString *timeString =[[NSString alloc] initWithFormat:#"%d", timeTick];
self.display.text = timeString;
if(timeTick <= 0){
[timer invalidate];
}
}
I want to show user that the value initially is 0 than it increase to 1 than 2 than 3 sequence repeat after some seconds
Worked done by me
I write this code in viewdidload but it print 99 in label directly but i want to show user changing values from 0 to 99.
for(int i=0;i<100;i++){
_totalEffort.text=[NSString stringWithFormat:#"%d",i];
}
I think this will help you:
#interface ViewController (){
int i;
NSTimer *myTimer;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(setData)
userInfo:nil
repeats:YES];
i = 0;
}
-(void)setData{
_totalEffort.text=[NSString stringWithFormat:#"%d",i];
i = i + 1;
if(i == 100){
[myTimer invalidate];
myTimer = nil;
}
}
Based on your requirement you need to use NSTimer for update your label progress
Step 1: Create 2 variable in your #interface class
#interface BUViewController: UIViewController {
NSTimer *timerCount;
int progress;
}
Step 2: Start Timer in your viewDidLoad() with progress value 0.
progress = 0;
timerCount = [NSTimer timerWithTimeInterval:0.5 target:self selector:#selector(updateLabel) userInfo:nil repeats:YES];
Step 3: create method with timer update label text an check progress value in it and stop timer while its reach 100.
-(void)updateLable{
if (progress<100) {
lblProgress.text = [NSString stringWithFormat:#"%d",progress];
progress++;
}else{
[timerCount invalidate];
timerCount = nil;
}
}
Hope this will help you to achieve your expected output.
For this you can use NSTimer you need to update UILabel value in its selector method. You can use this code.
-(void)viewDidLoad{
[super viewDidLoad];
self.time = 0;
_labelTimer.text =#"0";
[self startTimer];
}
- (void)startTimer {
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerAction:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
}
- (void)timerAction:(NSTimer *)timer {
self.time++;
if (self.time == 100)
{
[self stopTimer];
}
_labelTimer.text = [NSString stringWithFormat:#"%d",self.time];
}
-(void)stopTimer{
[self.timer invalidate];
self.timer = nil;
}
Here is code for your problem .hope it helps you.
- (void)viewDidLoad
{
[super viewDidLoad];
counter = 0;
t = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(updateLabel) userInfo:nil repeats:YES];
}
-(void)updateLabel
{
for (int i = counter; i<counter+1; i++)
{
_totalEffort.text=[NSString stringWithFormat:#"%d",i];
}
counter++;
if (counter ==101)
{
[t invalidate];
}
}
You are blocking run loop with your cycle. You just need resume it in each iteration.
for(int i=0;i<100;i++){
_totalEffort.text=#(i).stringValue;
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
I made a stopwatch application recently and it had a few glitches.
If I hit the stop button twice in a row, the entire app would crash.
If I hit the start button twice in a row, the timer would run twice as fast and the stop button would stop working.
How do I fix this problem?
Here is the code in my .h file:
IBOutlet UILabel *time;
IBOutlet UILabel *time1;
IBOutlet UILabel *time2;
NSTimer *myTicker;
NSTimer *myTicker2;
NSTimer *myTicker3;
}
- (IBAction)start;
- (IBAction)stop;
- (IBAction)reset;
- (void)showActivity;
- (void)showActivity1;
- (void)showActivity2;
#end
and here is my code in the .m file:
- (IBAction)start {
myTicker = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(showActivity) userInfo:nil repeats:YES];
myTicker2 = [NSTimer scheduledTimerWithTimeInterval:.1 target:self selector:#selector(showActivity1) userInfo:nil repeats:YES];
myTicker3 = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:#selector(showActivity2) userInfo:nil repeats:YES];
}
- (IBAction)stop {
[myTicker invalidate];
[myTicker2 invalidate];
[myTicker3 invalidate];
}
- (IBAction)reset {
time.text = #"00";
time1.text = #"00";
time2.text = #"00";
}
- (void)showActivity {
int currentTime = [time.text intValue];
int newTime = currentTime + 1;
if (newTime == 60) {
newTime = 0;
}
time.text = [NSString stringWithFormat:#"%d", newTime];
}
- (void)showActivity1 {
int currentTime1 = [time1.text intValue];
int newTime1 = currentTime1 + 1;
if (newTime1 == 10) {
newTime1 = 0;
}
time1.text = [NSString stringWithFormat:#"%d", newTime1];
}
- (void)showActivity2 {
int currentTime2 = [time2.text intValue];
int newTime2 = currentTime2 + 1;
time2.text = [NSString stringWithFormat:#"%d", newTime2];
}
Set the stop button's userInterActionEnabled property to NO and the start button's to YES when the -stop method is fired. Then switch and set the stop button's userInterActionEnabled to YES and the start button's to NO when -start is fired.
You should create a private BOOL variable "isRunning", which is checked when clicked on Stop or Start like:
- (IBAction)stop {
if(!isRunning) return;
[myTicker invalidate];
[myTicker2 invalidate];
[myTicker3 invalidate];
self.isRunning = NO;
}
etc. Also ignoring user interactions is general a good idea (like CodaFi suggested), but only fights the symptoms ;-) You really should do both checks.