1. 程式人生 > >tableview列表中的cell單元格有倒計時的情況處理

tableview列表中的cell單元格有倒計時的情況處理

在專案開發中遇到在tableiview列表中的cell單元格要使用到倒計時功能,當處理不當時,即cell複用沒使用好時,會出現倒計時顯示異常。比如說第一個cell顯示且倒計時開始後,當tableview列表向上滾動,即第一個cell向上移動且移出螢幕,然後tableview列表再向下滾動,即第一個cell向下移動且又在螢幕顯示,這個時候你會發現倒計時又是從頭開始計時了。

為了避免這種情況的產生,在編碼過程中我們只需要這樣考慮,並這樣做就可以了

步驟1 自定義cell

1-1 在自定義cell中建立NSTimer進行倒計時(將每個cell中的倒計時在當前cell中進行)

1-2 在自定義cell中設定可以獲取該NSTimer的方法(便於對NSTimer計時器的釋放處理)

步驟2 在tableview列表所在檢視viewcontroller中定義四變數

2-1 變數1 NSTimer(設定為當前檢視中的計時器,用於進行倒計時計算時間間隔差)

2-2 變數2 NSMutableArray(用於儲存每個計時器,便於當前檢視釋放時,處理計時器)

2-3 變數3 NSInteger(當前時間點,與變數4計算時間間隔差)

2-4 變數4 NSInteger(變化時間點,在變數1中進行自減,即倒計時計算;同時與變數3計算時間變化間隔差)

步驟3 使用

3-1 在tableview中使用

3-1-1 在cellForRow方法中執行自定義cell的倒計時方法

3-1-2 在cellForRow方法中獲取自定義cell的計時器並儲存在NSMutableArray中

3-2 在viewcontroller檢視中使用

3-2-1 在viewWillDisapper方法中釋放儲存有計時器的陣列

詳見程式碼

1 自定義cell程式碼

1-1 .h檔案

#import <UIKit/UIKit.h>

@interface CountDownCell : UITableViewCell
{
    @private
    UILabel *label;
    
    NSTimer *coundTimer;   //計時器
    NSInteger currentTime; //計時時間,秒
}

///開始計時
- (void)coundDownStart:(NSInteger)time;

///釋放計時器
- (void)releaseTimer;

///獲取計時器,便於釋放計時器
- (NSTimer *)getCellTimer;

@end

1-2 .m檔案

#import "CountDownCell.h"

@implementation CountDownCell

- (void)awakeFromNib {
    // Initialization code
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self)
    {
        self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        
        label = [[UILabel alloc] initWithFrame:CGRectMake(10.0, 0.0, self.frame.size.width - 10.0 * 2, self.frame.size.height)];
        [self addSubview:label];
    }
    return self;
}

- (void)dealloc
{
    coundTimer = nil;
    NSLog(@"cell timer %@", coundTimer);
}

//開始計時
- (void)coundDownStart:(NSInteger)time
{
    currentTime = time;
    [self show];
    if (!coundTimer)
    {
        coundTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(coundDown:) userInfo:nil repeats:YES];
    }
}

- (void)coundDown:(NSTimer *)timer
{
    currentTime--;
    [self show];
}

- (void)show
{
    NSInteger day = currentTime / (24 * 60 * 60);
    NSInteger hour = (currentTime % (24 * 60 * 60)) / (60 * 60);
    NSInteger minute = ((currentTime % (24 * 60 * 60)) % (60 * 60)) / 60;
    NSInteger second = ((currentTime % (24 * 60 * 60)) % (60 * 60)) % 60;
    NSString *time = [NSString stringWithFormat:@"%d天%d時%d分鐘%d秒", day, hour, minute, second];
    NSString *timeStr = [NSString stringWithFormat:@"倒計時:%@", time];
    label.text = timeStr;
}

//釋放計時器
- (void)releaseTimer
{
    if (coundTimer)
    {
        if ([coundTimer isValid])
        {
            [coundTimer invalidate];
            coundTimer = nil;
        }
    }
}

///獲取計時器
- (NSTimer *)getCellTimer
{
    return coundTimer;
}

@end

2 viewcontroller程式碼

2-1 .h檔案

#import <UIKit/UIKit.h>

@interface TableCountdownVC : UIViewController

@end


2-2 .m檔案

#import "TableCountdownVC.h"
#import "CountDownCell.h"

@interface TableCountdownVC () <UITableViewDataSource, UITableViewDelegate>

@property (nonatomic, strong) UITableView *mainTableView;
@property (nonatomic, strong) NSMutableArray *mainArray;

@property (nonatomic, strong) NSMutableArray *timerArray; //儲存timer陣列,便於釋放計時器
@property (nonatomic, strong) NSTimer *mainTimer;         //計時器,控制計時間隔

@property (nonatomic, assign) NSTimeInterval sourceTime;  //時間初始點,與currtentTime的時間差為變化時間間隔
@property (nonatomic, assign) NSTimeInterval currtentTime;//時間變化後時點,與sourceTime的時間差為變化時間間隔

@end

@implementation TableCountdownVC

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.title = @"cell倒計時複用";
    
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"reload" style:UIBarButtonItemStyleDone target:self action:@selector(buttonClick:)];
    
    [self setlocalData];
    
    [self setUI];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)loadView
{
    [super loadView];
    self.view.backgroundColor = [UIColor whiteColor];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    
    [self killTimer];
}

- (void)dealloc
{
    self.mainTimer = nil;
    NSLog(@"timer %@", self.mainTimer);
}

#pragma mark -建立檢視

- (void)setUI
{
    if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)])
    {
        [self setEdgesForExtendedLayout:UIRectEdgeNone];
    }
    
    self.mainTableView = [[UITableView alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.frame.size.width, self.view.frame.size.height) style:UITableViewStylePlain];
    [self.view addSubview:self.mainTableView];
    self.mainTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
    self.mainTableView.backgroundColor = [UIColor lightTextColor];
    self.mainTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
    self.mainTableView.delegate = self;
    self.mainTableView.dataSource = self;
}

#pragma mark -資料

- (void)setlocalData
{
    if (self.mainArray)
    {
        [self.mainArray removeAllObjects];
    }
    else
    {
        self.mainArray = [NSMutableArray array];
    }

    NSDate *date = [NSDate date];
    self.currtentTime = [date timeIntervalSinceReferenceDate];
    self.sourceTime = self.currtentTime;
    for (int i = 0; i < 30; i++)
    {
        NSInteger time = arc4random() % (int)self.currtentTime + 1000;
        NSNumber *timeNumber = [NSNumber numberWithInteger:time];
        [self.mainArray addObject:timeNumber];
    }
    
    if (!self.timerArray)
    {
        self.timerArray = [NSMutableArray array];
    }
    else
    {
        [self killTimer];
    }
    
    if (self.mainTimer)
    {
        [self.mainTimer invalidate];
        self.mainTimer = nil;
    }
    self.mainTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(coundDown:) userInfo:nil repeats:YES];
    [self.timerArray addObject:self.mainTimer];
}

- (void)killTimer
{
    if (self.timerArray)
    {
        for (NSTimer *subTimer in self.timerArray)
        {
            if (subTimer)
            {
                [subTimer invalidate];
            }
        }
        
        [self.timerArray removeAllObjects];
    }
}

#pragma mark -響應事件

- (void)buttonClick:(UIButton *)button
{
    [self setlocalData];
    [self.mainTableView reloadData];
}

- (void)coundDown:(NSTimer *)timer
{
    self.currtentTime--;
}

#pragma mark -列表檢視

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.mainArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *const reuseCell = @"reuseCell";
    CountDownCell *cell = (CountDownCell *)[tableView dequeueReusableCellWithIdentifier:reuseCell];
    if (!cell)
    {
        cell = [[CountDownCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseCell];
    }
    
    NSNumber *timeNumber = [self.mainArray objectAtIndex:indexPath.row];
    NSInteger time = timeNumber.integerValue;
    time = time - (self.sourceTime - self.currtentTime);
    [cell coundDownStart:time];
    
    NSTimer *cellTimer = [cell getCellTimer];
    [self.timerArray addObject:cellTimer];

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}

@end