UICollectionViewCell、UITableViewCell 中添加倒计时 滚动屏幕 时间停止bug解决

collectionViewcell 写的活动促销倒计时
问题描述 :###

在屏幕滚动的时候,倒计时NSTimer 停止,滚动停止后,倒计时继续,这样就造成了时间差。

解决方案 :###

<p>先贴代码:</p>

#import <UIKit/UIKit.h>
#import "TimeCutView.h"
#import "ShopModel.h"
@interface ShopCell : UICollectionViewCell
//倒计时背景
@property (nonatomic,strong)UIImageView *timeBak;
//倒计时view
@property (nonatomic,strong) TimeCutView *countDown;

@end

@implementation ShopCell

- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        _countDown= [TimeCutView countDown];
        _timeBak = [[UIImageView alloc]initWithFrame:CGRectMake(0, _specialImg.frame.size.height-30, _specialImg.frame.size.width, 30)];
        _timeBak.image = [UIImage imageNamed:@"daojishibg"];
        
        UILabel *timeCutLabel = [[UILabel alloc]initWithFrame:CGRectMake(5, 0, 85, 30)];
        timeCutLabel.text = @"距结束还有:";
        timeCutLabel.textColor = [UIColor whiteColor];
        timeCutLabel.font = [UIFont systemFontOfSize:13];
        
        _countDown = [[TimeCutView alloc]init];
        _countDown.frame = CGRectMake(75, 0, 100, 30);
        [self addSubview:timeCutLabel];
        [self addSubview:_countDown];
        [self addSubview:_timeBak];
    }
    return self;
}
@end

#import <UIKit/UIKit.h>
typedef void(^TimerStopBlock)();

@interface TimeCutView : UIView
// 时间戳
@property (nonatomic,assign)NSInteger timestamp;
// 背景
@property (nonatomic,copy)NSString *backgroundImageName;
// 时间停止后回调
@property (nonatomic,copy)TimerStopBlock timerStopBlock;
@property (nonatomic,strong) UILabel *separateLabel;
/**
 *  创建单例对象
 */
+ (instancetype)shareCountDown;// 工程中使用的倒计时是唯一的
/**
 *  创建非单例对象
 */
+ (instancetype)countDown; // 工程中倒计时不是唯一的
@end
#import "TimeCutView.h"
// label数量
#define labelCount 3
#define separateLabelCount 2
#define padding 6
@interface TimeCutView (){
    // 定时器
    NSTimer *timer;
}
@property (nonatomic,strong)NSMutableArray *timeLabelArrM;
@property (nonatomic,strong)NSMutableArray *separateLabelArrM;
// day
@property (nonatomic,strong)UILabel *dayLabel;
// hour
@property (nonatomic,strong)UILabel *hourLabel;
// minues
@property (nonatomic,strong)UILabel *minuesLabel;
// seconds
@property (nonatomic,strong)UILabel *secondsLabel;
@end

@implementation TimeCutView

// 创建单例
+ (instancetype)shareCountDown{
    static id instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[TimeCutView alloc] init];
    });
    return instance;
}

+ (instancetype)countDown{
    return [[self alloc] init];
}

- (instancetype)initWithFrame:(CGRect)frame{
    if (self = [super initWithFrame:frame]) {
        [self addSubview:self.dayLabel];
        [self addSubview:self.hourLabel];
        [self addSubview:self.minuesLabel];
        [self addSubview:self.secondsLabel];
        
        for (NSInteger index = 0; index < separateLabelCount; index ++) {
            _separateLabel = [[UILabel alloc] init];
            _separateLabel.text = @":";
            _separateLabel.textAlignment = NSTextAlignmentCenter;
            _separateLabel.textColor = [UIColor whiteColor];
            _separateLabel.font = [UIFont systemFontOfSize:13];
            [self addSubview:_separateLabel];
            [self.separateLabelArrM addObject:_separateLabel];
        }
    }
    return self;
}

- (void)setBackgroundImageName:(NSString *)backgroundImageName{
    
}

// 拿到外界传来的时间戳
- (void)setTimestamp:(NSInteger)timestamp{
    _timestamp = timestamp;
    if (_timestamp != 0) {
        timer =[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timer:) userInfo:nil repeats:YES];
        //##########################解决滑动时计时器停止###########################
        //    UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保
        //                     证界面滑动时不受其他 Mode 影响(操作 UI 界面的情况下运行)
        //    NSRunLoopCommonModes :这是一个占位用的 Mode,不是一种真正的 Mode
        //                     (RunLoop无法启动该模式,设置这种模式下,默认和操作 UI
        //                     界面时线程都可以运行,但无法改变 RunLoop 同时只能在一种
        //                     模式下运行的本质)
        //        NSDefaultRunLoopMode : App 的默认 Mode,通常主线程是在这个 Mode
        //                     下运行(默认情况下运行)
        //##########################解决滑动时计时器停止###########################
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    }
}

-(void)timer:(NSTimer*)timerr{
    _timestamp--;
    [self getDetailTimeWithTimestamp:_timestamp];
    if (_timestamp == 0) {
        [timer invalidate];
        timer = nil;
        // 执行block回调
        self.timerStopBlock();
    }
}

- (void)getDetailTimeWithTimestamp:(NSInteger)timestamp{
    NSInteger ms = timestamp;
    NSInteger ss = 1;
    NSInteger mi = ss * 60;
    NSInteger hh = mi * 60;
    NSInteger dd = hh * 24;
    
    // 剩余的
    NSInteger day = ms / dd;// 天
    NSInteger hour = (ms - day * dd) / hh;// 时
    NSInteger minute = (ms - day * dd - hour * hh) / mi;// 分
    NSInteger second = (ms - day * dd - hour * hh - minute * mi) / ss;// 秒
    //    NSLog(@"%zd日:%zd时:%zd分:%zd秒",day,hour,minute,second);
    
    if (day == 0) {
        // 获得view的宽、高
        CGFloat viewW = self.frame.size.width;
        CGFloat viewH = self.frame.size.height;
        // 单个label的宽高
        CGFloat labelW = viewW / labelCount;
        CGFloat labelH = viewH;
        self.dayLabel.text = @"";
        [self.hourLabel setFrame:CGRectMake(0, 0, labelW, labelH)];
        [self.minuesLabel setFrame:CGRectMake(labelW, 0, labelW, labelH)];
        [self.secondsLabel setFrame:CGRectMake(2 * labelW , 0, labelW, labelH)];
        for (NSInteger index = 0; index < self.separateLabelArrM.count ; index ++) {
            _separateLabel = self.separateLabelArrM[index];
            [_separateLabel setFrame: CGRectMake((labelW - 1) * (index + 1), 0, 5, labelH)];
        }
        self.hourLabel.text = [NSString stringWithFormat:@"%zd时",hour];
        self.minuesLabel.text = [NSString stringWithFormat:@"%zd分",minute];
        self.secondsLabel.text = [NSString stringWithFormat:@"%zd秒",second];
    }else{
        self.dayLabel.text = [NSString stringWithFormat:@"%zd天",day];
        self.hourLabel.text = [NSString stringWithFormat:@"%zd时",hour];
        self.minuesLabel.text = [NSString stringWithFormat:@"%zd分",minute];
        self.secondsLabel.text = [NSString stringWithFormat:@"%zd秒",second];
    }
    
    
}

- (void)layoutSubviews{
    [super layoutSubviews];
    // 获得view的宽、高
    CGFloat viewW = self.frame.size.width;
    CGFloat viewH = self.frame.size.height;
    // 单个label的宽高
    CGFloat labelW = viewW / labelCount;
    CGFloat labelH = viewH;
    self.dayLabel.frame = CGRectMake(0, 0, labelW, labelH);
    self.hourLabel.frame = CGRectMake(labelW, 0, labelW, labelH);
    self.minuesLabel.frame = CGRectMake(2 * labelW , 0, labelW, labelH);
    self.secondsLabel.frame = CGRectMake(3 * labelW, 0, labelW, labelH);
    
    for (NSInteger index = 0; index < self.separateLabelArrM.count ; index ++) {
        _separateLabel = self.separateLabelArrM[index];
        _separateLabel.frame = CGRectMake((labelW - 1) * (index + 2), 0, 5, labelH);
    }
}


#pragma mark - setter & getter

- (NSMutableArray *)timeLabelArrM{
    if (_timeLabelArrM == nil) {
        _timeLabelArrM = [[NSMutableArray alloc] init];
    }
    return _timeLabelArrM;
}

- (NSMutableArray *)separateLabelArrM{
    if (_separateLabelArrM == nil) {
        _separateLabelArrM = [[NSMutableArray alloc] init];
    }
    return _separateLabelArrM;
}

- (UILabel *)dayLabel{
    if (_dayLabel == nil) {
        _dayLabel = [[UILabel alloc] init];
        _dayLabel.textAlignment = NSTextAlignmentCenter;
        _dayLabel.textColor = [UIColor whiteColor];
        _dayLabel.font = [UIFont systemFontOfSize:13];
    }
    return _dayLabel;
}

- (UILabel *)hourLabel{
    if (_hourLabel == nil) {
        _hourLabel = [[UILabel alloc] init];
        _hourLabel.textAlignment = NSTextAlignmentCenter;
        _hourLabel.textColor = [UIColor whiteColor];
        _hourLabel.font = [UIFont systemFontOfSize:13];
    }
    return _hourLabel;
}

- (UILabel *)minuesLabel{
    if (_minuesLabel == nil) {
        _minuesLabel = [[UILabel alloc] init];
        _minuesLabel.textAlignment = NSTextAlignmentCenter;
        _minuesLabel.textColor = [UIColor whiteColor];
        _minuesLabel.font = [UIFont systemFontOfSize:13];
    }
    return _minuesLabel;
}

- (UILabel *)secondsLabel{
    if (_secondsLabel == nil) {
        _secondsLabel = [[UILabel alloc] init];
        _secondsLabel.textAlignment = NSTextAlignmentCenter;
        _secondsLabel.textColor = [UIColor whiteColor];
        _secondsLabel.font = [UIFont systemFontOfSize:13];
    }
    return _secondsLabel;
}


@end

在计时器中使用了NSRunLoop来解决时间停止的问题

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// 在默认模式下添加的 timer 当我们拖拽 scrollerView 的时候,不会运行 run 方法
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 

// 在 UI 跟踪模式下添加 timer 当我们拖拽 scrollerView 的时候,run 方法才会运行 
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode]; 

// timer 可以运行在两种模式下,相当于上面两句代码写在一起 
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响(操作 UI 界面的情况下运行)
NSRunLoopCommonModes :这是一个占位用的 Mode,不是一种真正的 Mode (RunLoop无法启动该模式,设置这种模式下,默认和操作 UI 界面时线程都可以运行,但无法改变 RunLoop 同时只能在一种模式下运行的本质)
NSDefaultRunLoopMode : App 的默认 Mode,通常主线程是在这个 Mode 下运行(默认情况下运行)

文中引用了这位同学提供的相关知识<a href="http://www.jianshu.com/p/338594ec008f" title="">http://www.jianshu.com/p/338594ec008f</a>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容