質問

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.

他のヒント

You could have your custom cell listen for a notification that the table view is going away and then post that notification in the viewWillDisappear method of your view controller.

When the parent TableView is popped of of the NavigationController that I assume you're using, the TableView should be dealloc'ed. In addition, each of its TableViewCells should also be dealloc'ed.

You could invalidate the timer and process any pending updates inside of the TableViewCell's dealloc method.

Without changing the design, that's what I would try. If that proves to be problematic, you might also try setting up some sort of communication back to the TableView when this value changes and have the TableView be responsible for the data updates.

It's hard to tell from your description, but I don't think you need to use the timer at all. Can't you just assign that stepper value to a property or ivar of that class, and then in the viewDidDisappear method, write that value out to core data?

I've found the best way to do this in the past is to set the UITableViewCell Subclass as a delegate of the view controller... then in your view controllers viewDidDisappear just call the delegate method.

The delegate method would be implemented within your UITableViewCell Subclass and would simply call the same code that you do when your two second timer completes. This is similar to the notification method suggested by Phillip, but a little bit cleaner if you're used to assigning delegates, etc.

Just override dealloc method and do the stuff. UITableViewCell is deallocated when parents view is popped / unloaded.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top