Based on what J. Costa suggested, I've stayed with the Grand Central Dispatch approach. The piece that I was missing (and I'm not sure that I explained this requirement very well) is the access to a shared resource.
In the following code, I've structured it so:
BOOL
shared resource to indicate whether or not the drip-feed should continue- create a serial queue for reading from / writing to that shared resource
- the drip-feed happens on a background queue and additionally uses the serial queue to check if it should continue
- the delayed view animation is setup using
dispatch_after
, and when it occurs it uses the serial queue to signal the drip-feeding should stop
Code:
NSString* inputText = @"Some meaningful text...";
dispatch_queue_t serialQueue = dispatch_queue_create("serialqueue", DISPATCH_QUEUE_SERIAL);
// the shared resource
_continueDripFeed = YES;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSInteger i = 0; i < inputText.length; i++) {
__block BOOL keepGoing = NO;
dispatch_sync(serialQueue, ^{
// read from the shared resource
keepGoing = _continueDripFeed;
});
if (keepGoing) {
[NSThread sleepForTimeInterval:0.02f];
NSString* charAtIndex = [inputText substringWithRange:NSMakeRange(i, 1)];
dispatch_async(dispatch_get_main_queue(), ^{
_textView.text = [NSString stringWithFormat:@"%@%@", _textView.text, charAtIndex];
});
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
_textView.text = inputText;
});
break;
}
}
});
double delayInSeconds = 0.5f;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
dispatch_sync(serialQueue, ^{
// update the shared resource
_continueDripFeed = NO;
});
[self animateTextViewToFrame:_offScreenFrame];
// Continue with workflow...
});