我已经找到了这个问题的几个答案,但它们并没有很大帮助。我正在尝试使用标准字幕单元格创建一个在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
答案 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
}
}