Ios 在表格视图中使用计时器可在任何滚动或表格重新加载后重新创建计时器

Ios 在表格视图中使用计时器可在任何滚动或表格重新加载后重新创建计时器,ios,objective-c,xcode,uitableview,countdown,Ios,Objective C,Xcode,Uitableview,Countdown,我正在使用 在我的Tableview CellForRowatinex中,使用如下计时器: UILabel *lblTimer=(UILabel *)[cell viewWithTag:10]; MZTimerLabel *UpgradeTimer = [[MZTimerLabel alloc] initWithLabel:lblTimer andTimerType:MZTimerLabelTypeTimer]; [UpgradeTimer setCountDownTime:timestamp]

我正在使用 在我的Tableview CellForRowatinex中,使用如下计时器:

UILabel *lblTimer=(UILabel *)[cell viewWithTag:10];
MZTimerLabel *UpgradeTimer = [[MZTimerLabel alloc] initWithLabel:lblTimer andTimerType:MZTimerLabelTypeTimer];
[UpgradeTimer setCountDownTime:timestamp];
[UpgradeTimer startWithEndingBlock:^(NSTimeInterval timestamp) {
lblTimer.text = @"✔";
}];
但在任何表格重新加载或滚动后,计时器的行为都很奇怪,似乎它会重新生成多个计时器,以便在同一位置进行计数。 使用此计时器时,应如何修复此问题

谢谢你的帮助


Elias

我看了一下MZTimerLabel,它严重违反了MVC。它将属于模型的东西(倒计时的计时器)放入视图中。这就是你的问题所在。应该能够重新创建视图,而不会对模型产生副作用

我建议你放弃这个类,创建自己的类。实现这样的目标其实很容易

  • 创建一个保存标题和结束日期的新类
  • 将该类的实例存储在支持表的模型中
  • 创建一个刷新tableView的计时器
  • 设置你的单元格
  • 这基本上就是表中基本倒计时所需的全部代码。由于它不在视图中存储任何数据,因此您可以随意滚动:

    @interface Timer : NSObject
    @property (strong, nonatomic) NSDate *endDate;
    @property (strong, nonatomic) NSString *title;
    @end
    
    @implementation Timer
    @end
    
    @interface MasterViewController () {
        NSArray *_objects;
        NSTimer *_refreshTimer;
    }
    @end
    
    @implementation MasterViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        NSMutableArray *modelStore = [NSMutableArray arrayWithCapacity:30];
        for (NSInteger i = 0; i < 30; i++) {
            Timer *timer = [[Timer alloc] init];
            timer.endDate = [NSDate dateWithTimeIntervalSinceNow:i*30];
            timer.title = [NSString stringWithFormat:@"Timer %ld seconds", (long)i*30];
            [modelStore addObject:timer];
        }
        _objects = modelStore;
    }
    
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        [_refreshTimer invalidate]; // timer should not exist, but just in case.
        _refreshTimer = [NSTimer timerWithTimeInterval:0.5f target:self selector:@selector(refreshView:) userInfo:nil repeats:YES];
    
        // should fire while scrolling, so we need to add the timer manually:
        [[NSRunLoop currentRunLoop] addTimer:_refreshTimer forMode:NSRunLoopCommonModes];
    }
    
    - (void)viewDidDisappear:(BOOL)animated {
        [super viewDidDisappear:animated];
        [_refreshTimer invalidate];
        _refreshTimer = nil;
    }
    
    - (void)refreshView:(NSTimer *)timer {
        // only refresh visible cells
        for (UITableViewCell *cell in [self.tableView visibleCells]) {
            NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
            [self configureCell:cell forRowAtIndexPath:indexPath];
        }
    }
    
    #pragma mark - Table View
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return _objects.count;
    }
    
    - (void)configureCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
        Timer *timer = _objects[indexPath.row];
        cell.textLabel.text = timer.title;
        NSInteger timeUntilEnd = (NSInteger)[timer.endDate timeIntervalSinceDate:[NSDate date]];
        if (timeUntilEnd <= 0) {
            cell.detailTextLabel.text = @"Finished";
        }
        else {
            NSInteger seconds = timeUntilEnd % 60;
            NSInteger minutes = (timeUntilEnd / 60) % 60;
            NSInteger hours = (timeUntilEnd / 3600);
            cell.detailTextLabel.text = [NSString stringWithFormat:@"%02ld:%02ld:%02ld", (long)hours, (long)minutes, (long)seconds];
        }
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
        [self configureCell:cell forRowAtIndexPath:indexPath];
        return cell;
    }
    
    @end
    
    @接口计时器:NSObject
    @属性(强,非原子)NSDate*endDate;
    @属性(强,非原子)NSString*标题;
    @结束
    @实现计时器
    @结束
    @接口MasterViewController(){
    NSArray*_对象;
    NSTimer*\u刷新计时器;
    }
    @结束
    @主视图控制器的实现
    -(无效)viewDidLoad
    {
    [超级视图下载];
    NSMutableArray*modelStore=[NSMutableArray阵列容量:30];
    对于(NSInteger i=0;i<30;i++){
    定时器*定时器=[[Timer alloc]init];
    timer.endDate=[NSDate dateWithTimeIntervalSinceNow:i*30];
    timer.title=[NSString stringWithFormat:@“计时器%ld秒”(长)i*30];
    [modelStore addObject:timer];
    }
    _对象=模型库;
    }
    -(无效)视图将显示:(BOOL)动画{
    [超级视图将显示:动画];
    [\u refreshTimer invalidate];//计时器不应存在,但以防万一。
    _refreshTimer=[NSTimer timerWithTimeInterval:0.5f目标:自选择器:@selector(refreshView:)userInfo:nil repeats:YES];
    //应该在滚动时触发,因此我们需要手动添加计时器:
    [[NSRunLoop currentRunLoop]addTimer:_refreshtTimerformode:NSRunLoopCommonModes];
    }
    -(无效)视图消失:(BOOL)已设置动画{
    [超级视窗消失:动画];
    [_刷新计时器无效];
    _刷新计时器=零;
    }
    -(无效)刷新视图:(NSTimer*)计时器{
    //仅刷新可见单元格
    for(UITableViewCell*单元格位于[self.tableView visibleCells]中){
    nsindepath*indepath=[self.tableView indexPathForCell:cell];
    [self-configureCell:cell for RowatineXpath:indexPath];
    }
    }
    #pragma标记-表视图
    -(NSInteger)表视图:(UITableView*)表视图行数节:(NSInteger)节
    {
    返回_objects.count;
    }
    -(void)configureCell:(UITableViewCell*)RowatineXpath的单元格:(NSIndexPath*)indexPath{
    Timer*Timer=_对象[indexPath.row];
    cell.textlab.text=timer.title;
    NSInteger timeUntilEnd=(NSInteger)[timer.endDate-timeIntervalSinceDate:[NSDate-date]];
    
    如果(timeUntilEnd您想实现什么?由于每次显示单元格时都会调用
    tableView:cellForRowAtIndexPath:
    ,因此您不应该在其中使用计时器。我的tableView单元格中有一个升级按钮,当用户点击该按钮时,我应该用倒计时来替换该按钮。有时还需要重新加载列表ed,因为数据来自Web服务。如果你使用自定义单元格,那么你可以在单元格中添加一个按钮,并在单击时隐藏它并显示计时器。我知道,伙计。这个问题比你想象的要复杂一些。无论如何,谢谢你的回复。非常感谢。我的类太大太复杂了,需要一点时间来完成请尝试您的解决方案。我正在开始使用它,并将在完成后接受您的回答。谢谢。嗨,Matthias,我已通过上一个链接重定向到您的问题:。我让您的计时器工作正常。但是,只有在我上下滚动
    UITableView
    时,时间才会刷新。是否有办法让它们在重新启动时刷新al time?意味着用户可以看到时间每秒钟都在减少。如果你能帮助我,那就太好了。谢谢!我尝试了以下方法来让我的UILabel实时更新:
    dispatch\u async(dispatch\u get\u main\u queue(),{()->Void in cell.timeLeft.text=NSString(格式:“%dh%dm%ds”,Int(小时),Int(分钟),Int(seconds))as String})
    但似乎仍然无法使其工作。任何帮助都将非常有用!谢谢!如何在swift 4.2中实现请帮助我解决此问题。