在tableview单元格中运行秒表计时器

时间:2015-09-25 17:09:09

标签: ios objective-c uitableview timer

我已经找到了这个问题的几个答案,但它们并没有很大帮助。我正在尝试使用标准字幕单元格创建一个在tableview的每个单元格中都有一个运行秒表计时器的应用程序。计时器名称显示在文本字段中,运行计时器显示在副标题中。每个单元格需要显示不同的时间格式(秒,分钟,小时,天,月或年)。计时器启动后经过的时间计算在每次计时器到期时完成,结果放入单元格的副标题中。只要使用一个计时器,代码就可以正常工作。但是,如果启动第二个计时器,则第一个单元中的运行计时器将重置为零。我相信这是因为我正在保存指向在cellForRowAtIndexPath中出列的单元格的指针:并且在计时器到期时使用此指针填充单元格。这可能违反了MVC。但是,当我的视图控制器委托实现协议时,我必须在tableview请求它时返回一个单元格,并且在计时器到期之前我不能这样做,以便我可以更新单元格中的字幕。如果不保存指向包含正在运行的计时器输出的单元格的指针,我看不到任何方法。我是iOS开发的新手,但我已经编程和最近使用故事板编写了这个项目,并得到了相同的结果。视图控制器的代码如下。请注意,item.timerType是从另一个视图控制器设置的,该控制器在故事板中被传递,并且数据被传递回主视图控制器。那部分工作正常。

感谢您的帮助!

    //
//  timerItem.h
//  LifeLogger
//
//  Created by Nelson Capes on 9/24/15.
//  Copyright © 2015 Nelson Capes. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface timerItem : NSObject

@property NSString *timerName;
@property NSTimeInterval interval;
@property NSString *timerOutput;
@property NSDate *startTime;
@property NSTimer *timer;
@property NSInteger timerType;
@property UITableViewCell *cell;
@end

    //
//  NRCTimerListTableViewController.h
//  LifeLogger
//
//  Created by Nelson Capes on 9/21/15.
//  Copyright © 2015 Nelson Capes. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "AddTimerItemViewController.h"
@interface NRCTimerListTableViewController : UITableViewController <UITabBarDelegate, UITableViewDataSource>
-(IBAction)listToAdd:(UIStoryboardSegue *)segue;
@property NSTimer *timer;
@property(strong) timerItem *item;
@property NSDate *startTime;



@end

    //
//  NRCTimerListTableViewController.m
//  LifeLogger
//
//  Created by Nelson Capes on 9/21/15.
//  Copyright © 2015 Nelson Capes. All rights reserved.
//

#import "NRCTimerListTableViewController.h"
#import "timerItem.h"
@interface NRCTimerListTableViewController ()


@property NSMutableArray *items;


@end

@implementation NRCTimerListTableViewController

// control comes here when user hits the Save button in AddTimerItemViewController.
// 
-(IBAction)listToAdd:(UIStoryboardSegue *)segue{
    if(!self.items){
        self.items = [[NSMutableArray alloc]init];}
    if(self.item != nil)
    {
    [self.items addObject:self.item];
        [self.tableView reloadData];
    }
}
-(void)loadInitialData{
    // get a timerItem object to pass onto to subsequent view controllers

    // and add it to items array

}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self loadInitialData];



}

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

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    return [self.items count];;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ListPrototypeCell" forIndexPath:indexPath];

    // Configure the cell...
    // First, get a pointer to the timer item from the items array


    switch (self.item.timerType) {
        case 0:
        {
            NSTimeInterval interval = 1;
            self.item.interval = interval;
        }
            break;
        case 1:
        {
            NSTimeInterval interval = 60;
            self.item.interval = interval;
        }
            break;
        case 2:
        {
            NSTimeInterval interval = 3600;
            self.item.interval = interval;
            break;
        }
        case 3:
        {
            NSTimeInterval interval = (3600 * 24);
            self.item.interval = interval;
            break;
        }
        case 4:
        {
            NSTimeInterval interval = (36600 * 24 * 30);
            self.item.interval = interval;
            break;
        }
        case 5:
        {
            NSTimeInterval interval = (3600 * 24 * 365);
            self.item.interval = interval;
            break;
        }
        default:
            break;
    }


        self.item.cell = cell;

      //  cell.textLabel.text = self.item.timerName;

        // set the start time in the item
        self.item.startTime = [NSDate date];
        NSTimeInterval startInterval = self.item.interval;
        self.item.timer = [NSTimer scheduledTimerWithTimeInterval:startInterval target:self selector:@selector(timerCount:) userInfo:self.item repeats:YES];

        [[NSRunLoop currentRunLoop] addTimer:self.item.timer forMode:NSRunLoopCommonModes];

        // debug ///////////////////////////////////////////////
        NSLog(@"Item %@ timer started", self.item.timerName);
        ////////////////////////////////////////////////////////
        [self.item.timer fire];

    // set the textLabel in the cell to the itemName
        return cell;
}

-(void)timerCount:t
    {

        NSTimer *timer = t;
        timerItem *item = timer.userInfo;
        NSLog(@"Item %@ timer fired", item.timerName);
        NSTimeInterval endInterval = [item.startTime timeIntervalSinceNow];
        endInterval = (-1 * endInterval);

        switch (item.timerType) {
            case 0:
            {
                int time = round(endInterval);
                div_t h = div(time, 3600); //seconds total, divided by 3600 equals
                int hours = h.quot;         // hours, divided by 60 equals
                div_t m = div(h.rem, 60);   // minutes
                int minutes = m.quot;
                int seconds = m.rem;        // and remainder is seconds
                NSString *intervalString = [NSString stringWithFormat:@"%d hours, %d minutes, %d seconds", hours, minutes, seconds];

                NSString *outputString = [intervalString stringByAppendingString:@" ago"];
                item.timerOutput = outputString;

            }
                break;
            case 1:
            {
                int time = round(endInterval);
                div_t h = div(time, 3600);      // seconds total, divided by 3600 equals
                int hours = h.quot;             // hours, divided by 60 equals
                div_t m = div(h.rem, 60);       // minutes
                int minutes = m.quot;
                NSString *intervalString = [NSString stringWithFormat:@"%d hours, %d minutes", hours, minutes];
                NSString *outputString = [intervalString stringByAppendingString:@" ago"];
                item.timerOutput = outputString;
            }
                break;
            case 2:
            {
                int time = round(endInterval);
                div_t h = div(time, 3600); // seconds total, divided by 3600 equals
                int hours = h.quot;        // hours
                NSString *intervalString = [NSString stringWithFormat:@"%d hours", hours];
                NSString *outputString = [intervalString stringByAppendingString:@" ago"];
                item.timerOutput = outputString;
            }
                break;
            case 3:
            {
                int time = round(endInterval);

                div_t h = div(time, 3600); // seconds total, divided by 3600 equals
                int hours = h.quot;        // hours, divided by 24 equals
                div_t d =div(h.rem, 24);   // days
                int days = d.quot;
                NSString *intervalString = [NSString stringWithFormat:@"%d days, %d hours", days, hours];
                NSString *outputString = [intervalString stringByAppendingString:@" ago"];
                item.timerOutput = outputString;
            }
                break;
            case 4:
            {
                int time = round(endInterval);
                div_t h = div(time, 3600); // seconds total, divided by 3600 equals
                __unused int hours = h.quot;        // hours, divided by 24 equals
                div_t d =div(h.rem, 24);   // days
                int days = d.quot;
                div_t y = div(d.rem, 12);// divided by 12 equals months
                int months = y.quot;
                NSString *intervalString = [NSString stringWithFormat:@"%d months, %d days", months, days];
                NSString *outputString = [intervalString stringByAppendingString:@" ago"];
                item.timerOutput = outputString;
            }
                break;
            case 5:
            {
                int time = round(endInterval);
                div_t h = div(time, 3600); // seconds total, divided by 3600 equals
                __unused int hours = h.quot;        // hours, divided by 24 equals
                div_t d =div(h.rem, 24);   // days
                int days = d.quot;
                div_t y = div(d.rem, 365);// divided by 365 equals years
                int years = y.quot;
                NSString *intervalString = [NSString stringWithFormat:@"%d years, %d days", years, days];
                NSString *outputString = [intervalString stringByAppendingString:@" ago"];
                item.timerOutput = outputString;
            }
                break;
        }
        item.cell.textLabel.text = item.timerName;
        item.cell.detailTextLabel.text = item.timerOutput;

    }




/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    // Return NO if you do not want the specified item to be editable.
    return YES;
}
*/

/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the row from the data source
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    } else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }   
}
*/

/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
}
*/

/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
    // Return NO if you do not want the item to be re-orderable.
    return YES;
}
*/

#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
    if([segue.identifier isEqualToString:@"listToAdd"]){
        self.item = [[timerItem alloc]init];
        AddTimerItemViewController *destViewController = segue.destinationViewController;
        destViewController.timerItem = self.item;}
}
/*
#pragma mark - Table view delegate
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath{


}
 */
@end

2 个答案:

答案 0 :(得分:0)

这样的代码一直有效,直到您填写了适合屏幕的最大单元格数后滚动tableView。例如,在iPhone 6和单元格高度为60的情况下,我可以在屏幕上显示大约11行。我的行中的所有计时器都很好。但是,如果我向后滚动以查看第一个单元格,则会发生不可预测的事情 - 某些单元格会重置其计时器。如果计时器设置为一秒钟,这只是非常明显。在更长的时间间隔(例如,一分钟),这不是一个问题,但我怀疑洞仍然存在。这是我正在使用的代码:

    -(void) calculateTimer:(NSTimer *)theTimer

{
    self.timerItem = [theTimer userInfo];


        // for date only cell
        if(self.timerItem.timerType == 0){
            [theTimer invalidate];
        }

        // if this cell is visible and is the one for which the name and type were set, put the name into the cell
        for(NRCItemCell *cell in [self.tableView visibleCells])
        {
            //NSLog(@"cell = %@", cell);

            NSIndexPath *ip = [self.tableView indexPathForCell:cell];
            NSUInteger row = [[[NRCItemStore sharedStore]allItems] indexOfObjectIdenticalTo:self.timerItem];

            NSLog(@"ip = %@, row = %lu", ip, (unsigned long)row);

            if (row == ip.row){
                cell.timerName.text = self.timerItem.timerName;

                // timerType set by TimerTypeTableView Controller as follows:

                // 0 - date
                // 1 - seconds elapsed
                // 2 - minutes elapsed
                // 3 - hours elapsed
                // 4 - days elapsed
                // 5 - months elapsed
                // 6 - years elapsed

                switch (self.timerItem.timerType) {
                    case 0:{
                        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
                        [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
                        [dateFormatter setTimeStyle:NSDateFormatterNoStyle];

                        NSDate *date = [NSDate date];
                        NSString *formattedDateString = [dateFormatter stringFromDate:date];
                        cell.timer.text = formattedDateString;
                    }
                        break;
                    case 1:
                    {
                        NSTimeInterval interval = [self.timerItem.startTime timeIntervalSinceNow];
                        interval = (-1 * interval);
                        int time = round(interval);
                        div_t h = div(time, 3600); //seconds total, divided by 3600 equals
                        int hours = h.quot;         // hours, divided by 60 equals
                        div_t m = div(h.rem, 60);   // minutes
                        int minutes = m.quot;
                        int seconds = m.rem;        // and remainder is seconds
                        //  NSLog(@"%d:%d:%d", hours, minutes, seconds);
                        //NSString *intervalString = [NSString stringWithFormat:@"%ld", (long)time];
                        NSString *intervalString = [NSString stringWithFormat:@"%d hours, %d minutes, %d seconds", hours, minutes, seconds];

                        NSString *outputString = [intervalString stringByAppendingString:@" ago"];
                        cell.timer.text = outputString;

                    }
                        break;
                    case 2:
                    {
                        NSTimeInterval interval = [self.timerItem.startTime timeIntervalSinceNow];
                        interval = (-1 * interval);
                        int time = roundf(interval);
                        div_t h = div(time, 3600);      // seconds total, divided by 3600 equals
                        int hours = h.quot;             // hours, divided by 60 equals
                        div_t m = div(h.rem, 60);       // minutes
                        int minutes = m.quot;
                        NSString *intervalString = [NSString stringWithFormat:@"%d hours, %d minutes", hours, minutes];
                        NSString *outputString = [intervalString stringByAppendingString:@" ago"];

                        cell.timer.text = outputString;

                    }
                        break;
                    case 3:
                    {
                        NSTimeInterval interval = [self.timerItem.startTime timeIntervalSinceNow];
                        interval = (-1 * interval);
                        int time = roundf(interval);
                        div_t h = div(time, 3600); // seconds total, divided by 3600 equals
                        int hours = h.quot;        // hours
                        NSString *intervalString = [NSString stringWithFormat:@"%d hours", hours];
                        NSString *outputString = [intervalString stringByAppendingString:@" ago"];

                        cell.timer.text = outputString;
                    }
                        break;
                    case 4:
                    {
                        NSTimeInterval interval = [self.timerItem.startTime timeIntervalSinceNow];
                        interval = (-1 * interval);
                        int time = roundf(interval);
                        div_t h = div(time, 3600); // seconds total, divided by 3600 equals
                        int hours = h.quot;        // hours, divided by 24 equals
                        div_t d =div(h.rem, 24);   // days
                        int days = d.quot;
                        NSString *intervalString = [NSString stringWithFormat:@"%d days, %d hours", days, hours];
                        NSString *outputString = [intervalString stringByAppendingString:@" ago"];

                        cell.timer.text = outputString;
                    }
                        break;
                    case 5:
                    {
                        NSTimeInterval interval = [self.timerItem.startTime timeIntervalSinceNow];
                        interval = (-1 * interval);
                        int time = roundf(interval);
                        div_t h = div(time, 3600); // seconds total, divided by 3600 equals
                        __unused int hours = h.quot;        // hours, divided by 24 equals
                        div_t d =div(h.rem, 24);   // days
                        int days = d.quot;
                        div_t y = div(d.rem, 12);// divided by 12 equals months
                        int months = y.quot;
                        NSString *intervalString = [NSString stringWithFormat:@"%d months, %d days", months, days];
                        NSString *outputString = [intervalString stringByAppendingString:@" ago"];
                        cell.timer.text = outputString;
                    }
                        break;
                    case 6:
                    {
                        NSTimeInterval interval = [self.timerItem.startTime timeIntervalSinceNow];
                        interval = (-1 * interval);
                        int time = roundf(interval);
                        div_t h = div(time, 3600); // seconds total, divided by 3600 equals
                        __unused int hours = h.quot;        // hours, divided by 24 equals
                        div_t d =div(h.rem, 24);   // days
                        int days = d.quot;
                        div_t y = div(d.rem, 365);// divided by 365 equals years
                        int years = y.quot;
                        NSString *intervalString = [NSString stringWithFormat:@"%d years, %d days", years, days];
                        NSString *outputString = [intervalString stringByAppendingString:@" ago"];
                        cell.timer.text = outputString;
                    }
                        break;
                }

            }
        }
    }

答案 1 :(得分:-1)

您只需要一个计时器来进行更新(每秒,十分之一秒或其他)。然后,您只需跟踪何时以及是否已启动单元格。因此,为每个单元格创建一个类似self.timestamps的数组,其中包含开始时间[NSDate date],如果没有启动,则为零或其他内容。如果它停止了,你甚至可以让它成为一个NSString,最新的时间作为一个字符串,这样你就可以在cellForItemAtIndexPath中设置这个值:只需在你的计时器中跳过它,检查它是否是一个NSString而不是比NSDate。

然后在您的计时器中使用NSArray * visibleCells = [tableView visibleCells]来获取当前显示的单元格列表。循环遍历该列表获取UITableViewCells并使用IndexPath * ip = [tableView indexPathForCell:cell],然后您可以使用它来获取该单元格的正确计时器。然后更新该单元格的视图(标题,副标题或其他)。

你的计时器中有这样的东西

NSDate *now = [NSDate date];

for (UITableViewCell *cell in [self.tableView visibleCells]) {
  NSIndexPath *ip = [self.tableview indexPathForCell:cell];
  id object = [self.timestamps objectForIndex:ip.row];

  if(object && [object isKindOfClass:[NSDate class]]) {

    // It's started
    NSDate timeStampForCell = (NSDate)timeStampForCell;
    NSTimeInterval seconds = [now timeIntervalSinceDate:timeStampForCell];
    [cell setDetailTextLabel:[NSString stringWithFormat:@"%d", (int)seconds]];

    } else if([object isKindOfClass:[NSString class]]){

    // It's stopped

    } else {

    // It's never been started

    }
}