Reputation: 6357
I am showing a UIView to display information about map features that a user has marked for other purposes, such as deleting or editing. When a feature is marked, i am showing a UIView containing info and an undo option. Showing the the UIView for a fixed amount of time is done and working, however, I would like the delay to have a sliding expiration, that is, if the user selects another map feature to mark, the delay should reset itself and the UIView should stay visible for the full amount of time specified. What I am looking for is a sliding expiration. My code thus far:
double delaySeconds = 15;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delaySeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self toggleUndoViewToShow:NO];
});
So, my delay time is 15 seconds and if, during this 15 seconds, another feature is marked, I would like the view to remain visible for the full delay time. How might I go about this? thanks!
Upvotes: 0
Views: 395
Reputation: 90561
Use a timer dispatch source. Create it with dispatch_source_create()
, passing DISPATCH_SOURCE_TYPE_TIMER
as the type. Call dispatch_source_set_timer()
to set the start time. To emulate dispatch_after()
, which only runs its task once, use DISPATCH_TIME_FOREVER
for the interval. Call dispatch_source_set_event_handler()
to set the task to perform when the designated time arrives. Also, the task needs to cancel the source using dispatch_source_cancel()
to clean up the source. Call dispatch_resume()
on the source to allow it to go (it's created in a suspended state).
You can use dispatch_source_set_timer()
at any time to adjust the time at which the task should run. You can also cancel it at any time.
Since this is somewhat more complex than dispatch_after()
, you can wrap it up in a helper to set this up for you. The caller of the helper function would submit a task block that's ignorant of the need to cancel the source. Your helper would wrap that in a task that calls the supplied block and then does the dispatch_source_cancel()
.
For the very simple case you need, the above may be overkill. You could also do something like:
__block int savedTaskID;
if ([NSThread isMainThread])
savedTaskID = ++self.currentTaskID; // an integer property on your view
else dispatch_sync(dispatch_get_main_queue(), ^{
savedTaskID = ++self.currentTaskID;
});
double delaySeconds = 15;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delaySeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (savedTaskID == self.currentTaskID)
[self toggleUndoViewToShow:NO];
});
If you want to delay the toggling, simply repeat the above steps. Since that increments currentTaskID
, when previously-submitted tasks run, they do nothing. This is a little wasteful, since the neutered tasks still stick around and eventually run, but it doesn't sound like it will happen much.
Upvotes: 3
Reputation: 343
From what I understood, you would need to cancel an existing block scheduled to execute after certain delay and schedule the equivalent block with fresh delay of 15 seconds. This should help you with canceling a delayed block scheduled to run.
Upvotes: 1