Search code examples
iosobjective-cnstimeruitableview

iOS Timer in tableview


I have tableview with multiple cells, each cell has a button, When the button is clicked the button is disabled and the timer starts for 30 sec. After the 30 sec is done the button should be enabled again. There is label under the button which should count down how many seconds left for the button to be enabled again.

1- The problem is when the user scroll the button gets enabled again, because the cell is reusable. How to avoid that?

2- how to show time left for that cell? .. Reusable cell causing the problem again

Sample of the code..

/// Table View Code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *simpleTableIdentifier = @"cell1";
    static NSString *simple2TableIdentifier = @"cell2";


    Cell1 *cell = (Cell1 *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
    Cell2 *cell2 = (Cell2 *)[tableView dequeueReusableCellWithIdentifier:simple2TableIdentifier];

    if (cell == nil) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"xxxxxx" owner:self options:nil];
        cell = [nib objectAtIndex:0];

    }
    if (cell2 == nil) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"yyyyyyyyy" owner:self options:nil];
        cell = [nib objectAtIndex:0];

    }

    if(![[[self.info objectAtIndex:indexPath.row]objectForKey:@"someKeyGoesHere"]isEqualToString:@""])
    {
        cell.some_id.text=[[self.info objectAtIndex:indexPath.row] objectForKey:@"order_id"];

        ///////////////////////////////////////////////
        if ([[[self.info objectAtIndex:indexPath.row] objectForKey:ccc] isEqualToString:@"2"] && ([tempo isEqualToString:uu] || [tempo isEqualToString:bbb]) )
        {
            cell.btn.hidden=NO;
            cell.btn.tag=[[[self.info objectAtIndex:indexPath.row] objectForKey:@"some_id"] integerValue];
            [cell.btn addTarget:self action:@selector(btnClicked:) forControlEvents:UIControlEventTouchUpInside];
        }
        else
        {
            cell.btn.hidden=YES;
        }

        if(!cell.btn.isHidden)
        {
            if([[Manager btns]containsObject:cell.btn])
            {
                cell.btn.enabled=NO;
            }
            else
            {
                cell.btn.enabled=YES;
            }
        }
        ///////////////////////////////////////////////


        return cell;

    }
    else
    {
        ///some code
        return cell2;
    }


}


-(void)btnClicked:(UIButton*)sender
{
    NSLog(@"A: %li",(long)sender.tag);
    [Manager startTimer:sender];

}


//Sigleton Code

-(void)startTimer:(UIButton*)btn
{
    NSLog(@"start");
    btn.enabled=NO;
    [btns addObject:btn];
    NSLog(@"O_O: %li",(long)btn.tag);
    [NSTimer scheduledTimerWithTimeInterval:10
                                     target:self
                                   selector:@selector(stopTimer:)
                                   userInfo:nil
                                    repeats:NO];
}
-(void)stopTimer:(NSTimer *)timer
{

    NSLog(@"stop");
    [timer invalidate];
    ((UIButton *)[btns firstObject]).enabled=YES;
    NSLog(@"O_O: %li",(long)((UIButton *)[btns firstObject]).tag);
    [btns removeObject:[btns firstObject]];

}

Thanks in advance.


Solution

  • You have to think about it like this - my model does the work, the cell only shows what is going on.

    Therefore, you add the timer outside of the scope of UITableViewCell, and in cellForRow you'll "only" refer to your model and adjust the cell accordingly (enabled/disabled). If you want it to tick down (~ show live countdown), you need to fire NSNotification and catch it with the cells view to have it ticking down. Or then there are always frameworks such as ReactiveCocoa or SwiftBond where you bond views to certain logic and they react upon it.

    If you don't separate it, you'll end up with exactly what you're describing - the reusable cell will reset itself.