ios - Running stopwatch timer in a tableview cell -


i have found couple of answers question, not helpful. trying create app has running stopwatch timer in each cell of tableview, using standard subtitle cells. timer name displayed in textfield , running timer displayed in subtitle. each cell needs display different time format (seconds, minutes, hours, days, months, or years). calculation of time elapsed since timer started done each time timer expires, , result put cell's subtitle. code works fine long 1 timer used. however, if second timer started, running timer in first cell reset zero. believe because saving pointer cell dequeued @ cellforrowatindexpath: , using pointer populate cell when timer expires. violates mvc. however, view controller delegate implements protocol, have return cell when tableview requests it, , can't until timer expires can update subtitle in cell. don't see way without saving pointer cell containing output of running timer. new ios development have written project both programmatically , using storyboard , same results. code view controller below. note item.timertype set view controller segued in storyboard data passed main view controller. part works ok.

thanks help!

    // //  timeritem.h //  lifelogger // //  created nelson capes on 9/24/15. //  copyright © 2015 nelson capes. 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 nelson capes on 9/21/15. //  copyright © 2015 nelson capes. 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 nelson capes on 9/21/15. //  copyright © 2015 nelson capes. rights reserved. //  #import "nrctimerlisttableviewcontroller.h" #import "timeritem.h" @interface nrctimerlisttableviewcontroller ()   @property nsmutablearray *items;   @end  @implementation nrctimerlisttableviewcontroller  // control comes here when user hits 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{     // timeritem object pass onto subsequent view controllers      // , add items array  }  - (void)viewdidload {     [super viewdidload];     [self loadinitialdata];    }  - (void)didreceivememorywarning {     [super didreceivememorywarning];     // dispose of resources can 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 cell...     // first, pointer timer item 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 start time in 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 textlabel in cell 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 3600 equals                 int hours = h.quot;         // hours, divided 60 equals                 div_t m = div(h.rem, 60);   // minutes                 int minutes = m.quot;                 int seconds = m.rem;        // , remainder 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 3600 equals                 int hours = h.quot;             // hours, divided 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 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 3600 equals                 int hours = h.quot;        // hours, divided 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 3600 equals                 __unused int hours = h.quot;        // hours, divided 24 equals                 div_t d =div(h.rem, 24);   // days                 int days = d.quot;                 div_t y = div(d.rem, 12);// divided 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 3600 equals                 __unused int hours = h.quot;        // hours, divided 24 equals                 div_t d =div(h.rem, 24);   // days                 int days = d.quot;                 div_t y = div(d.rem, 365);// divided 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 support conditional editing of table view. - (bool)tableview:(uitableview *)tableview caneditrowatindexpath:(nsindexpath *)indexpath {     // return no if not want specified item editable.     return yes; } */  /* // override support editing table view. - (void)tableview:(uitableview *)tableview commiteditingstyle:(uitableviewcelleditingstyle)editingstyle forrowatindexpath:(nsindexpath *)indexpath {     if (editingstyle == uitableviewcelleditingstyledelete) {         // delete row data source         [tableview deleterowsatindexpaths:@[indexpath] withrowanimation:uitableviewrowanimationfade];     } else if (editingstyle == uitableviewcelleditingstyleinsert) {         // create new instance of appropriate class, insert array, , add new row table view     }    } */  /* // override support rearranging table view. - (void)tableview:(uitableview *)tableview moverowatindexpath:(nsindexpath *)fromindexpath toindexpath:(nsindexpath *)toindexpath { } */  /* // override support conditional rearranging of table view. - (bool)tableview:(uitableview *)tableview canmoverowatindexpath:(nsindexpath *)indexpath {     // return no if not want item re-orderable.     return yes; } */  #pragma mark - navigation  // in storyboard-based application, want little preparation before navigation - (void)prepareforsegue:(uistoryboardsegue *)segue sender:(id)sender {     // new view controller using [segue destinationviewcontroller].     // pass selected object 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 

my code works until scroll tableview after maximum number of cells can fit on screen have been filled. example, on iphone 6 , cell height of 60, can fit 11 rows on screen. of timers in rows fine point. but, if scroll see first cells, unpredictable things happen - of cells have timers reset. noticeable if timers set 1 second. @ longer interval (say, minute), isn't of problem suspect hole still there. code using:

    -(void) calculatetimer:(nstimer *)thetimer  {     self.timeritem = [thetimer userinfo];           // date cell         if(self.timeritem.timertype == 0){             [thetimer invalidate];         }          // if cell visible , 1 name , type set, put name 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 timertypetableview controller 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 3600 equals                         int hours = h.quot;         // hours, divided 60 equals                         div_t m = div(h.rem, 60);   // minutes                         int minutes = m.quot;                         int seconds = m.rem;        // , remainder 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 3600 equals                         int hours = h.quot;             // hours, divided 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 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 3600 equals                         int hours = h.quot;        // hours, divided 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 3600 equals                         __unused int hours = h.quot;        // hours, divided 24 equals                         div_t d =div(h.rem, 24);   // days                         int days = d.quot;                         div_t y = div(d.rem, 12);// divided 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 3600 equals                         __unused int hours = h.quot;        // hours, divided 24 equals                         div_t d =div(h.rem, 24);   // days                         int days = d.quot;                         div_t y = div(d.rem, 365);// divided 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;                 }              }         }     } 

Comments