Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Display StopWatch Timer in UITableViewCell dynamically

I want to hold timer values and Display it from start in new UITableViewCell but my problem is that I am successfully able to display stopwatch timer on first Cell but when I am trying to add a new cell in UITableView so my timer is set to second cell and I am unable to define how to keep my first timer alive.

I thought of managing an Array but I want to call reload TableData everytime, but I think that is bad :(

I tried work around as below but I am not figuring out how to show timerValue on previous cell with working stopwatch dynamic from same count and new cell timerValue from start.

A static solution but my rows are inserting dynamically and deleting aswell from TableView so static solution limits me for specific count of rows, and looking for dynamic solution.

    - (void) putDelayOnTimeOut {


            if (!_timerQueue) {
                _timerQueue = dispatch_queue_create("timer_queue", NULL);
            }
            dispatch_async(_timerQueue, ^{

                self.startTime = [NSDate date];

                if (globalRowIndex == 0) {

                    _timer = [NSTimer scheduledTimerWithTimeInterval:0.6 target:self selector:@selector(showTimeoutActivity:) userInfo:nil repeats:YES];
                } else if (globalRowIndex == 1) {
                    _timer = [NSTimer scheduledTimerWithTimeInterval:0.6 target:self selector:@selector(showTimeoutActivity1:) userInfo:nil repeats:YES];
                }

                //[[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
                [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];

                //[[NSRunLoop currentRunLoop] run];
                [_timer fire];
            });

    }


    - (void)showTimeoutActivity:(id)unused
    {
        if( [_timer isValid] )
        {
            NSTimeInterval interval = [self.startTime timeIntervalSinceNow];
            interval = (-1 * interval);

            NSString *timerString = [self formatInterval:interval];
            NSLog(@"%@", timerString);

            MyCell *cell = (MyCell *)[self.activeCallTbl viewWithTag:globalRowIndex+10];
            //NSIndexPath *indexPath = [self.activeCallTbl indexPathForCell:cell];
            //NSLog(@"%d",indexPath.row);
            cell.timerLbl.text = timerString;

        }
    }


 - (void)showTimeoutActivity1:(id)unused
    {
        if( [_timer isValid] )
        {
            NSTimeInterval interval = [self.startTime timeIntervalSinceNow];
            interval = (-1 * interval);


            NSString *timerString = [self formatInterval:interval];
            NSLog(@"%@", timerString);

            MyCell *cell = (MyCell *)[self.activeCallTbl viewWithTag:globalRowIndex+10];
            //NSIndexPath *indexPath = [self.activeCallTbl indexPathForCell:cell];
            //NSLog(@"%d",indexPath.row);
            cell.timerLbl.text = timerString;

        }
    }
like image 283
user366584 Avatar asked Dec 08 '22 14:12

user366584


1 Answers

I'm not sure to have understood what you are asking for exactly...but I try.

One simple solution can be to subclass your UITableViewCell and add the timer to the cell itself. So the cell will have the reference to the timer and will create a new one every time a new cell is added to the TableView

I attach example code: Link (I've added 3 cells statically, but you can add as you want, simply tap on one cell to start the timer, if you tap again the timer starts again from 0)

Create your custom cell subclass and add some methods like these:

- (void) startTimer
{

    // invalidate a previous timer in case of reuse
    if (self.timer)
        [self.timer invalidate];


    self.startTime = [NSDate date];

    // create a new timer
    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.6 target:self selector:@selector(calculateTimer) userInfo:nil repeats:YES];

    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

    [self.timer fire];
}

- (void)calculateTimer
{
    NSTimeInterval interval = [self.startTime timeIntervalSinceNow];
    interval = (-1 * interval);

    NSString *intervalString = [NSString stringWithFormat:@"%f", interval];

    self.timerLabel.text = intervalString;
}

Then, in the UITableViewController implementation, simply call

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

    TimerCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
    [cell startTimer];
}

For more details ask or look at the attached project.

This is the quickest way I've found, although someone would prefer to leave the timer code and reference inside the Table View Controller (But you'll need more code)

EDIT

As written in the comment by Spynet this won't work if you have more than 7/8 cells (the number of cells that can fit the screen), because when you scroll the UITableView cells are reused. So a single cell will be used in different indexPaths, so starting a timer on the first cell, then scrolling, will cause you to see the timer already started every 7/8 cells (because it's the timer of the previously created cell)

To solve this there are many ways. One could be to remove the timer from the cell and create an array of timers in the ViewController. One timer for each indexpath. Another way (but I don't recomed it if the cells are many) is to don't reuse cells.

Starting from my Example Project:
1 - create a nib file (call it TimerCell.xib) and copy/paste the cell prototype inside it from the storyboard
2 - add this property to the TimerViewController

@interface TimerViewController ()
@property (strong, nonatomic) NSMutableArray *cells;
@end

3 - in the initWithCoder method initialize the array:

- (id)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        _cells = [NSMutableArray array];
    }
    return self;
}

4 - update the cellForRowAtIndexPath method and change it like this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

    TimerCell *cell = nil;

    if ([self.cells count] > indexPath.row)
    {
        cell = [self.cells objectAtIndex:indexPath.row];
    } else {
        cell = [[[NSBundle mainBundle] loadNibNamed:@"TimerCell" owner:self options:nil] objectAtIndex:0];
        [self.cells addObject:cell];
    }

    // Configure the cell...

    return cell;
}

In this way the cells won't be reused

like image 150
LombaX Avatar answered Feb 01 '23 14:02

LombaX