注意:请不要评论这是否是一个好主意;)我只是在尝试,所以想看看它是如何实现的......
我有一个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
在填充视图(包括UITextView
)屏幕外的动画之前填充0.3或0.5秒,然后才开始播放...
所以它有点像这样:
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
进行滴漏更新...
有什么建议吗?
答案 0 :(得分:3)
您可以延迟动画以“给”滴水的时间,如下所示:
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.5秒。这是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.
}
}];
答案 1 :(得分:1)
最好不要排除大量任务,然后设置另一个延迟动画。是的,它会起作用,但它不是那么可维护,并且不适合将来的其他情况。
相反,请考虑使用NSTimer
和几个实例变量(可以将其包含在另一个类中以保持一切干净整洁)。实例变量基本上是当前进度(来自循环的i
)。每次计时器触发时,请检查i
- 如果文本动画未完成,请使用i
子串并更新UI。如果文本动画完成,则使计时器无效并启动最终视图动画。
通过这种方式,逻辑可以组织,易于理解,可重复使用和取消。
答案 2 :(得分:0)
根据J. Costa的建议,我一直坚持使用Grand Central Dispatch方法。我缺少的那篇文章(我不确定我是否很好地解释了这个要求)是对共享资源的访问。
在下面的代码中,我的结构是这样的:
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...
});