在项目开发中遇到在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
{
UILabel *label;
NSTimer *coundTimer; // 计时器
NSInteger currentTime; // 计时时间,秒
}
/// 开始计时
- (void)coundDownStart:(NSInteger)time;
/// 释放计时器
- (void)releaseTimer;
/// 获取计时器,便于释放计时器
- (NSTimer *)getCellTimer;
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];
}