2010-01-03 57 views
2

我可以从互联网下载ZIP文件。后处理在connectionDidFinishLoading中完成,并且工作正常,除非没有更新UIView元素。例如,我设置了statusUpdate.text = @“解压缩文件”,但直到connectionDidFinishLoading完成后才会显示该更改。同样,直到此方法结束,UIProgressView和UIActivityIndi​​catorView对象才会更新。connectionDidFinishLoading - 如何强制更新UIView?

有没有什么办法可以在这个方法中强制更新UIView?我尝试设置[self.view setNeedsDisplay],但没有奏效。它似乎在主线程中运行。所有其他命令在这里工作得很好 - 唯一的问题是更新用户界面。

谢谢!

更新:这里是不更新的UIView代码:

-(void)viewWillAppear:(BOOL)animated { 
    timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(processUpdate:) userInfo:nil repeats:YES]; 
    downloadComplete = NO; 
    statusText.text = @""; 

} 

-(void)processUpdate:(NSTimer *)theTimer { 
    if (! downloadComplete) { 
     return; 
    } 

    [timer invalidate]; 
    statusText.text = @"Processing update file."; 
    progress.progress = 0.0; 
     totalFiles = [newFiles count]; 
    for (id fileName in newFiles) { 
     count++; 
      progress.progress = (float)count/(float)totalFiles; 
     // ... process code goes here ... 
     } 
} 

在随后结束processUpdate的,我设置DownloadComplete等= YES。这种构建&运行没有错误,并按预期工作,除非UIVIEW没有任何更新,直到processUpdate完成之后,然后所有内容一次更新。

感谢您的帮助!

回答

0

当您在connectionDidFinishLoading中时,应用程序运行循环中没有其他任何事情发生。控制需要传递回运行循环,以便它可以编排UI更新。

只需将数据传输标记为完整并更新视图即可。推迟下载数据的繁重处理到它自己的线程。

应用程序会调用您的视图让它们稍后在运行循环中刷新它们的内容。根据需要在您自己的自定义视图上实施drawRect。

0

如果您在主线程中接收到connectionDidFinishLoading,那么您运气不好。除非您从此方法返回,否则在用户界面中将不会刷新任何内容。

UIProgressView *prog = ... <your progress view reference> ... 
[prog performSelectorOnMainThread:@selector(setProgress:) 
         withObject:[NSNumber numberWithFloat:0.5f] 
        waitUntilDone:NO]; 

要小心,不要从非主更新UI:

在另一方面,如果你运行在一个单独的线程上的连接,那么你可以安全地使用下面的代码更新UI线程 - 始终使用performSelectorOnMainThread方法!

1

正如Niels所说,如果你想查看视图更新,你必须将控制返回到运行循环。但除非你真的需要,否则不要开始分离新线程。我推荐这种方法:

- (void)connectionDidFinishLoading:(NSConnection *)connection { 
    statusUpdate.text = @"Uncompressing file"; 
    [self performSelector:@selector(doUncompress) withObject:nil afterDelay:0]; 
} 

- (void)doUncompress { 
    // Do work in 100 ms chunks 
    BOOL isFinished = NO; 
    NSDate *breakTime = [NSDate dateWithTimeIntervalSinceNow:100]; 
    while (!isFinished && [breakTime timeIntervalSinceNow] > 0) { 
     // do some work 
    } 
    if (! isFinished) { 
     statusUpdate.text = // here you could update with % complete 
     // better yet, update a progress bar 
     [self performSelector:@selector(doUncompress) withObject:nil afterDelay:0]; 
    } else { 
     statusUpdate.text = @"Done!"; 
     // clean up 
    } 
} 

基本的想法是,你的工作在小块。您从方法返回以允许运行循环定期执行。对performSelector的调用将确保控件最终返回到您的对象。

请注意,这样做的风险是用户可能按某个按钮或以某种您可能不期望的方式与UI进行交互。在您工作时拨打UIApplication的beginIgnoringInteractionEvents以忽略输入可能会有帮助,除非您想要非常好,并提供一个取消按钮,设置一个标志,您可以检查您的doUncompress方法...

你也可以尝试自己运行运行循环,每隔一段时间调用[[NSRunLoop currentRunLoop] runUntilDate:...],但我从未在自己的代码中尝试过。

+0

谢谢你的回应!我按照描述(复制和粘贴)实现了它,但它在while()循环中挂起,并且从不输入它。我在模拟器上运行它。任何想法,为什么它挂? – waltcrit 2010-01-10 06:50:04

+0

我的循环条件错误(应该是'> 0'而不是'<0')。我确定了我的答案;也许这是你的代码有什么问题?否则,我没有足够的信息来说明。 – benzado 2010-01-11 02:37:00

+0

这工作,谢谢! – waltcrit 2010-01-12 23:59:02

0

正如你对计时器所做的一样,只需使用ConnectionDidFinish将你的处理代码发送到一个新线程即可。定时器可以更新UI,因为它们是从主线程运行的。