目标C语言 iOS -如何在一段时间后中断异步后台操作



注意:请不要评论这是不是一个好主意;)我只是在试验,所以想看看结果如何…

我有一个UITextView和一些inputText,我将把它们设置为UITextView的文本。

我想尝试在视觉上滴入inputText到文本视图,例如,一次一个字母或一次一个单词。

例如(不考虑滴水馈送延迟或执行线程):

for (NSInteger i = 0; i < inputText.length; i++) {
    NSString* charAtIndex = [text substringWithRange:NSMakeRange(i, 1)];
    _textView.text = [NSString stringWithFormat:@"%@%@", _textView.text, charAtIndex];
}

因此,要构建延迟并查看添加的字符(或单词),每次添加一个,我可以执行以下操作:

dispatch_queue_t backgroundQueue = dispatch_queue_create("background_queue", 0);
for (NSInteger i = 0; i < inputText.length; i++) {
    dispatch_async(backgroundQueue, ^{
        [NSThread sleepForTimeInterval:0.01f];
        NSString* charAtIndex = [inputText substringWithRange:NSMakeRange(i, 1)];
        dispatch_async(dispatch_get_main_queue(), ^{
            _textView.text = [NSString stringWithFormat:@"%@%@", _textView.text, charAtIndex];
        });    
    });
}

这很好,正如预期的那样,for循环在后台队列上排队了一堆异步操作。

但是我希望上面的操作作为一个更大的、单个的同步可视化的一部分。在添加此代码以滴入UITextView之前,我只是在UITextView中设置文本,然后将当前视图(包括UITextView)动画化到屏幕外。都在主线程上。用户会点击一个按钮,看到整个文本出现,然后视图开始动画离开屏幕,马上。然后,用户转到工作流的下一步。

我正在尝试滴水馈送可视化给人的印象文本视图被填充一个字符(或单词)的时间,但我不希望用户不得不等到每一个最后的字符/单词已添加。所以,我希望看到UITextView被填充,说,0.3或0.5秒,开始动画,动画视图(包括UITextView)屏幕外…

就像这样:

dispatch_queue_t backgroundQueue = dispatch_queue_create("background_queue", 0);
for (NSInteger i = 0; i < inputText.length; i++) {
    dispatch_async(backgroundQueue, ^{
        [NSThread sleepForTimeInterval:0.01f];
        NSString* charAtIndex = [inputText substringWithRange:NSMakeRange(i, 1)];
        dispatch_async(dispatch_get_main_queue(), ^{
            _textView.text = [NSString stringWithFormat:@"%@%@", _textView.text, charAtIndex];
        });    
    });
}
// ...
// Animate the view (including the UITextView) off screen
// ...
// User continues with the main workflow

现在,所有的滴进都在后台异步发生,一旦我添加代码使视图动画化,你就会错过视觉滴进。主线程一直运行到将视图动画移出屏幕。

我不确定如何实现我想要的?

我应该以某种方式中断上面的循环吗?检查从另一个线程更新的标志?

我不能把等待放在主线程上-因为它会阻止滴灌更新到UITextView

有什么建议吗?

你可以延迟动画来给滴注时间,像这样:

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){
    // ...
    // Animate the view (including the UITextView) off screen
    // ...              
});

这样用户将看到0.5s的滴灌动画。这是GCD版本,但是你也可以使用这个方法:

[UIView animateWithDuration:0.3f // Set your duration here
                      delay:0.5f
                    options:UIViewAnimationOptionCurveEaseOut // Choose the right option here
                 animations:^{
                     // Do your animations here.
                 }
                 completion:^(BOOL finished){
                     if (finished) {
                         // Do your method here after your animation.
                     }
                 }];

你最好不要排队的任务负载,然后设置另一个延迟动画。是的,它可以工作,但是它不是那么可维护,并且不能很好地满足未来的其他情况。

相反,可以考虑使用NSTimer和几个实例变量(可以将它们包装在另一个类中以保持一切干净整洁)。实例变量基本上是当前进程(来自循环的i)。每次计时器触发时,检查i -如果文本动画没有完成,使用i子字符串并更新UI。如果文本动画已经完成,则取消计时器并开始最后的视图动画。

通过这种方式,逻辑是有组织的,易于理解,可重用和可取消的。

根据J. Costa的建议,我仍然使用中央调度的方法。我缺少的部分(我不确定我是否很好地解释了这个需求)是对共享资源的访问。

在下面的代码中,我这样组织它:

  • BOOL共享资源指示滴注是否继续
  • 创建一个串行队列用于读写共享资源
  • 滴入发生在后台队列上,并且另外使用串行队列来检查它是否应该继续
  • 延迟视图动画是使用dispatch_after设置的,当它发生时,它使用串行队列来信号滴注应该停止
代码:

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...
});

相关内容

  • 没有找到相关文章

最新更新