【问题标题】: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

【问题讨论】:

标签: ios objective-c loops uiimageview


【解决方案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 以在我的模拟器上进行测试。
    • 哦。好的。我实际上在想我如何看待它。 :)
    猜你喜欢
    • 2021-05-30
    相关资源
    最近更新 更多