0

I have a NSTimer that works fine and counts down in 1 second intervals. But I want the timer to trigger immediately without that 1 second delay.

I thought calling [timer fire] should work for this (described here) but it doesn't make a difference. I want the timer to be triggered as fast as if I scheduled the interval to be 0.

- (void)onStartButtonPressed:(UIButton*)sender
{
    CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:tv];
    NSIndexPath* indexPath = [tv indexPathForRowAtPoint:buttonPosition];
    NSInteger index = indexPath.row;

//  starts timer for cell at the index path
if (indexPath != nil)
{
    NSTimer* timer = [timerArray objectAtIndex: index];
    if ([timer isEqual:[NSNull null]]) {
        NSLog(@"It's empty");

        // start timer
        NSTimer timer = [NSTimer   scheduledTimerWithTimeInterval:1.0
                                                            target:self
                                                          selector:@selector(onTick:)
                                                          userInfo:indexPath
                                                           repeats:YES];

//            [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

        NSLog(@"before timer fire");
        [timer fire];


        // update data array of timer objects and countdowns
        NSInteger selectedTimeIdx = [[selectedTimeIdxArray objectAtIndex: index] integerValue];
        NSInteger selectedTime = [pickerTimeArray[selectedTimeIdx] integerValue];
        [timerArray replaceObjectAtIndex:index withObject:timer];
        [countdownArray replaceObjectAtIndex:index withObject:[NSNumber numberWithInteger:selectedTime*60]];
    } else {
        NSLog(@"It's not empty");
    }
}

- (void)onTick:(NSTimer *)timer
{
    NSLog(@"on tick method starts");

    // get the timer's owner's index path and update label
    NSIndexPath* indexPath = [timer userInfo];
    NSInteger index = indexPath.row;

    // update countdown
    NSInteger countdown = [[countdownArray objectAtIndex: index] integerValue];
    [countdownArray replaceObjectAtIndex:index withObject:[NSNumber numberWithInteger:--countdown]];


//    NSLog(@"countdown: %ld", (long)countdown);


    [tv reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];


//    NSLog(@"Tic indexPath: %@", indexPath);


    if (countdown == 0)
    {
        [timer invalidate];
        timer = nil;
    }
}

The timer works but I don't want there to be a 1 second delay for it to be initially triggered. I want the timer to start immediately.

Edit: I added logs that rmaddy suggested. Here are my results (I changed the interval time to 3):

2015-05-19 14:41:02.827 restaurant[4206:77915] before timer fire

2015-05-19 14:41:02.827 restaurant[4206:77915] on tick method starts

2015-05-19 14:41:05.827 restaurant[4206:77915] on tick method starts

2015-05-19 14:41:08.828 restaurant[4206:77915] on tick method starts

Community
  • 1
  • 1
May Yang
  • 523
  • 1
  • 5
  • 18
  • 1
    Add some log statements. 1) Add one at the start of the `onTick:` method. 2) Add one just before calling `[timer fire]`. Run your code. Paste the output from the first log and the first few logs from `onTick:` into your question so we can see the time stamps. – rmaddy May 19 '15 at 21:30
  • I can't reproduce this. The following trivial app (just put this in ViewController.m and wire it up) seems to work just like you request: https://gist.github.com/rnapier/fe84f1df50dd2b8e51e8. Please simplify your code to a Minimal, Complete, and Verifiable example (http://stackoverflow.com/help/mcve). – Rob Napier May 19 '15 at 21:32
  • 3
    (Also note that the above code won't compile. `timer` is missing a `*`. That suggests this is not exactly your code. – Rob Napier May 19 '15 at 21:33
  • Now this question makes no sense with the log methods. First, the messages logged aren't in the code snippet provided. Second, the "before" and first "on tick" happen at the exact same time (it's almost hard to believe they happen at the *exact* same time stamp). Third, there appears to be a **3** second interval between the remaining message (but your code suggests it should be *1* second). – nhgrif May 19 '15 at 21:46
  • 1
    Your edits are slightly helpful... but can you *really* not see that your log statements are happening *exactly* as you expect? The first two *both* happen at **2015-05-19 14:41:02.827** – nhgrif May 19 '15 at 21:55
  • @nhgrif yes i get it, i'm looking at it now. now i see something is up with how my timer label is being updated – May Yang May 19 '15 at 21:56

2 Answers2

1

Why not just call your method before the timer

[self onTick:nil];
//usual timer code here

Edit: as stated by rmaddy

NSTimer *timer = [NSTimer   scheduledTimerWithTimeInterval:1.0
                                                        target:self
                                                      selector:@selector(onTick:)
                                                      userInfo:indexPath repeats:YES];

[self onTick:timer];
EagerMike
  • 2,032
  • 1
  • 24
  • 40
  • 1
    Call this in place of the `[timer fire];` line and you can pass in `timer` instead of `nil`. – rmaddy May 19 '15 at 21:22
  • @nhgrif It's not deprecated and it problem does work. There's really not enough detail in the question to know for sure. – rmaddy May 19 '15 at 21:26
  • @rmaddy I can add some more code if helpful. I tried to pass [self onTick:timer] but it still has the same problem. – May Yang May 19 '15 at 21:28
  • 3
    @MayYang it sounds like whatever reason you have for thinking that `onTick:` hasn't been called is flawed. Put a breakpoint in it or have it `NSLog` something. – Tommy May 19 '15 at 21:35
  • @jammycoder there's nothing wrong with calling `-fire` here. That's good style IMO, and avoids repeating the selector. This should work, and in my version of it, `-fire` does work. – Rob Napier May 19 '15 at 21:36
  • @RobNapier I never said using `fire` was a problem. I was simply adding a comment to this answer that if you chose to use an explicit call to the selector instead of calling `fire`, you can still pass the timer instead of `nil`. – rmaddy May 19 '15 at 21:37
  • (Sorry @rmaddy; I misread the answerer. I was talking to @jammycoder; edited the comment) – Rob Napier May 19 '15 at 21:38
  • @RobNapier I agree, just offering an alternative to what seemed to be the problem at the time. There's obviously something else going on. – EagerMike May 19 '15 at 21:58
0

Some of the comments were really helpful, especially adding the logs as suggested by rmaddy. It turns out [timer fire] was working fine.

I had confused myself because my timerLabel which displayed the countdown was being updated in my cell with a 1 second delay, and I thought that meant the timer instantiation was delayed.

Once I saw what the problem really was, all I had to do was update the cell in the same block that I instantiated the timer (instead of just onTick).

I just added this to my onStartButtonPressed and everything works as I want it to.

        // update countdown
        NSInteger countdown = [[countdownArray objectAtIndex: index] integerValue];
        [countdownArray replaceObjectAtIndex:index withObject:[NSNumber numberWithInteger:--countdown]];

        // cell label gets updated right at start button press
        [tv reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

Leaving here in case it helps anyone.

May Yang
  • 523
  • 1
  • 5
  • 18