I have a UITableViewCell subclass which contains a UIStepper. When the user interacts with the stepper, I fire off an NSTimer so that the stepper will save its value and write to Core Data if the value is not changed again within 2 seconds.
To put it another way:
UIStepper is contained within UITableViewCell.
User can change stepper value up and down.
Each touch triggers timer with 2 second delay.
Each subsequent touch invalidates the timer.
When stepper has been left alone for 2 seconds, changes are saved.
This works very well for what I need it for. The problem is that if my user is very quick and they change the value, then pop the view controller and go to do something else, the 2 second timer still hasn't fired and the data is not up to date for the next action.
To keep things as simple as possible, I need to be able to tell within that table view cell if the (table) view is getting popped. Then I can expedite the saving process and ensure data is up to date and saved before any other actions take place.
If you want to tell if the table has been popped, put your cleanup/save method in the viewWillDisappear
method. Because you're using timers, you do not want to do it it dealloc
, so you don't have any unintended strong reference cycles.
It's not clear from your question, but I would want to make sure that you're not putting your NSTimer
on your UITableViewCell
cells. Obviously, it's a model issue, not a view issue, but also table views do all sorts of optimizations for dequeue and reusing table view cells.
Second, what ever object class you have to keep track of your data (I call it ModelDataItem
) should provide not only the mechanism to save, to use the timers, etc., but also a mechanism to force the save of any pending records (which I do through a boolean needSave
). So, to support that, in my mind, you ModelDataItem
probably should have at least the following four items:
(a) a reference to its own timer;
@property (nonatomic, strong) NSTimer *timer;
(b) a flag that indicates whether the record has a pending save operation
@property (nonatomic) BOOL needSave;
(c) a method that you call whenever the object's values change (e.g. the value was incremented) to schedule the save in 2 seconds:
- (void)scheduleSave
{
self.needSave = YES;
if (self.timer)
[self.timer invalidate];
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:@selector(save)
userInfo:nil
repeats:NO];
}
(d) you need the method that the timer calls that actually saves the record:
- (void)save
{
// do whatever you need to save the record
NSLog(@"%s saving value=%@", __FUNCTION__, self.value);
// now let's clean up the timer
if (self.timer)
{
[self.timer invalidate];
self.timer = nil;
}
self.needSave = NO;
}
Then, in your table view controller, you should:
(a) when the stepper's UIControlEventValueChanged
is called, you should obviously change your data model and then call the above ModelDataItem
method scheduleSave
;
(b) when the table view is being dismissed, should presumably promptly save anything pending:
for (ModelDataItem *item in allModelDataItems)
{
if (item.needSave)
[item save];
}
Note, on that last point, I do not rely on dealloc
to clean up and save the model items that need saving, because a scheduled NSTimer
retains its target
and thus the dealloc
won't get call (or at least not until the timer is performed). So, I manually iterate through them and take care of that when I dismiss the view.