如何为循环添加延迟?

Posted

技术标签:

【中文标题】如何为循环添加延迟?【英文标题】:How to add a delay to a loop? 【发布时间】:2013-07-18 19:03:35 【问题描述】:

我正在尝试使用此代码将图像视图添加到 UIView:

for (int i = 0; i <numberOfImages; i++) 
    UIImageView *image = [UIImageView alloc]initWithFrame:CGRectMake(40, 40, 40, 40)];
    image.image = [images objectAtIndex:i];
    [self.view addSubview:image];

这可行,但问题是我希望在添加每个图像之前有 5 秒的延迟,而不是同时添加它们。有人可以帮我吗?谢谢。

例子:

5 seconds = one image on screen
10 seconds = two images on screen
15 seconds = three images on screen

【问题讨论】:

Displaying images in sequence with a loop, Displaying multiple images serially within a loop的可能重复 【参考方案1】:

使用NSTimer 会更有效。

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:numberOfSeconds
                                                      target:self 
                                                    selector:@selector(methodToAddImages:) 
                                                    userInfo:nil 
                                                     repeats:YES];

这实际上会以指定的时间间隔重复调用methodToAddImages。要停止调用此方法,请调用[NSTimer invalidate](请记住,无效的计时器无法重复使用,您需要创建一个新的计时器对象以防重复此过程)。

methodToAddImages 中,您应该有代码来遍历数组并添加图像。 您可以使用计数器变量来跟踪索引。

另一种选择(我的建议)是拥有此数组的可变副本并将lastObject 添加为子视图,然后将其从数组的可变副本中删除。

您可以先按相反的顺序创建一个 mutableCopy,如下所示:

NSMutableArray* reversedImages = [[[images reverseObjectEnumerator] allObjects] mutableCopy];

你的 methodToAddImages 看起来像:

- (void)methodToAddImages

    if([reversedImages lastObject] == nil)
    
        [timer invalidate];
        return;
    

    UIImageView *imageView = [[UIImageView alloc] initWithFrame:(CGRectMake(40, 40, 40, 40))];
    imageView.image = [reversedImages lastObject];
    [self.view addSubview:imageView];
    [reversedImages removeObject:[reversedImages lastObject]];

我不知道您使用的是 ARC 还是 Manual Retain Release,但此答案是假设 ARC 编写的(基于您问题中的代码)。

【讨论】:

【参考方案2】:

您可以使用dispatch_after 调度一个块,异步执行以添加图像。示例:

for(int i = 0; numberOfImages; i++)

    double delayInSeconds = 5.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
    
        UIImageView *image = [UIImageView alloc]initWithFrame:CGRectMake(40, 40, 40, 40)];
        image.image = [images objectAtIndex:i];
        // Update the view on the main thread:
        [self.view performSelectorOnMainThread: @selector(addSubview:) withObject: image waitUntilDone: NO];
    );

【讨论】:

试过了,我认为它会工作,但它只是在延迟后同时添加所有图像。有什么想法吗? @SarabyteStudios 你说得对,我忘了任何视图都应该只在主线程上更新。所以我编辑了块中的最后一行代码,以确保添加子视图的方法在主线程上执行。【参考方案3】:

将您的代码分成一个函数,并通过NSTimer 调用。

for (int i = 0; numberOfImages; i++) 
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5
                                                      target:self
                                                    selector:@selector(showImage)
                                                    userInfo:[NSNumber numberWithInt:i] 
                                                     repeats:NO];

然后是你的函数:

-(void) showImage:(NSTimer*)timer 
      //do your action, using
      NSNumber *i = timer.userInfo;

      //Insert relevant code here

      if (!done)
          NSTimer *newTimer = [NSTimer scheduledTimerWithTimeInterval:5 
                                                      target:self
                                                    selector:@selector(showImage)
                                                    userInfo:[NSNumber numberWithInt: i.intValue+1]
                                                     repeats:NO];
  

userInfo 是一种将参数传递给您需要调用的函数的便捷方式(但它们必须是对象)。此外,通过使用repeats:NO,您不必担心会使计时器失效,也不会有让计时器在内存中运行的风险。

【讨论】:

由于您使用的是 showImage: 并递增 intValue,因此这里的初始循环似乎是多余的。没有它,这项技术也可以工作。 另外“selector:@selector(showImage)”实际上应该是“selector:@selector(showImage:)”【参考方案4】:

这也是最好的选择。试试这个

 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];

【讨论】:

您能否准确解释一下为什么最好按照您的方式实施时间延迟? @XLE_22 SRunLoop 更好,因为它允许NSRunLoop 在您等待时响应事件。【参考方案5】:

我觉得你最好用动画

for (int i = 0; i < numberOfImages; i++)

    // Retrieve the image
    UIImageView *image = [[UIImageView alloc] initWithFrame:CGRectMake(40, 40, 40, 40)];
    image.image = [images objectAtIndex:i];

    // Add with alpha 0
    image.alpha = 0.0;
    [self.view addSubview:image];

    [UIView animateWithDuration:0.5 delay:5.0*i options:UIViewAnimationOptionAllowUserInteraction animations:^
        // Fade in with delay
        image.alpha = 1.0;
     completion:nil];

不完全符合您的要求,因为所有视图都会立即添加,然后淡入,但我觉得您实际上正在尝试实现这一点,就像某种图像堆叠,对吧?

其实,如果你打算移除之前的图片,你可以在完成块中进行,像这样:

UIImageView *imagePrevious = nil;

for (int i = 0; i < numberOfImages; i++)

    // Retrieve the image
    UIImageView *image = [[UIImageView alloc] initWithFrame:CGRectMake(40, 40, 40, 40)];
    image.image = [images objectAtIndex:i];

    // Add with alpha 0
    image.alpha = 0.0;

    [UIView animateWithDuration:0.5 delay:5.0*i options:UIViewAnimationOptionAllowUserInteraction animations:^
        // Add and fade in with delay
        [self.view addSubview:image];
        image.alpha = 1.0;
     completion:^(BOOL finished)
    
        if (finished && imagePrevious)
        
            [imagePrevious removeFromSuperview];
        
    ];

    imagePrevious = image;

【讨论】:

虽然这不会创建 5 秒的时间间隔 @Rakesh 实际上,我将延迟写为可变的 (5.0*i)。 哎呀.. 我读它为 0.2*i。没关系。为“不明显”的解决方案 +1 :) 我想我实际上是在您评论时编辑问题,在此之前我添加了 0.2 以在我的模拟器上进行测试。 哦。好的。我实际上在想我如何看待它。 :)

以上是关于如何为循环添加延迟?的主要内容,如果未能解决你的问题,请参考以下文章

如何为 Rails 控制器添加延迟以进行测试?

如何为 DOM 中的每个元素添加不同的文本

如何为列0单元格django模板添加超链接

如何为每个 PHP 循环创建一个从表数据中设置变量的循环

为了戒网,我给每个网站自动添加3-25秒的访问延迟

如何为每个循环发出不同的元组并在风暴螺栓的单个字段方法中声明?