iOS:如何停止后台线程在主线程中更新UI?

4
我实现了一个UITableView,其中包含UIImageView单元格,每个单元格都会通过NSTimer定期刷新自身,每5秒钟一次。每个图像都是在后台线程中从服务器加载的,并且我还通过调用performSelectorOnMainThread从该后台线程更新UI,显示新图像。目前为止一切正常。
当前,当我不等待当前单元格中的图像加载完成并快速滚动到新单元格时,就会出现问题。然而,新单元格显示的是以前单元格的图像而不是新单元格的图像。尽管下一轮NSTimer将正确地显示图像,但这可能会使用户感到困惑。
如果我不重用UITableView的单元格,那么问题就会消失,但考虑到我的应用程序需要显示的单元格数量,这不是一个选项。
因此,我能想到的唯一解决方案是,如果我知道用户执行了滚动操作,就取消(或终止)即将显示旧图像的后台线程。
我想知道这可能不是最佳做法,因此寻求您的建议。
(我也不能使用SDWebImage,因为我的要求是循环显示从服务器加载的一组图像)
// In MyViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    ...
    NSTimer* timer=[NSTimer scheduledTimerWithTimeInterval:ANIMATION_SCHEDULED_AT_TIME_INTERVAL 
                                                target:self 
                                              selector:@selector(updateImageInBackground:) 
                                              userInfo:cell.imageView 
                                               repeats:YES];
    ...
}

- (void) updateImageInBackground:(NSTimer*)aTimer
{  
    [self performSelectorInBackground:@selector(updateImage:)
                       withObject:[aTimer userInfo]];
}  

- (void) updateImage:(AnimatedImageView*)animatedImageView 
{      
    @autoreleasepool { 
        [animatedImageView refresh];
    }
}  

// In AnimatedImageView.m
-(void)refresh
{
    if(self.currentIndex>=self.urls.count)
        self.currentIndex=0;

    ASIHTTPRequest *request=[[ASIHTTPRequest alloc] initWithURL:[self.urls objectAtIndex:self.currentIndex]];
    [request startSynchronous];

    UIImage *image = [UIImage imageWithData:[request responseData]];

    // How do I cancel this operation if I know that a user performs a scrolling action, therefore departing from this cell.
    [self performSelectorOnMainThread:@selector(performTransition:)
                       withObject:image
                    waitUntilDone:YES];
}

-(void)performTransition:(UIImage*)anImage
{
    [UIView transitionWithView:self duration:1.0 options:(UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionAllowUserInteraction) animations:^{ 
        self.image=anImage;
        currentIndex++;
    } completion:^(BOOL finished) {
    }];
}

1
WWDC 2012年的211个会议详细介绍了这一点,包括不同级别的队列取消、优化等。这是一个非常棒的视频。 - jrturton
cellForRow 中,为什么你不只是在没有图像时将其设置为 nil 呢?这样,它会暂时变成空白而不是显示错误的图像。 - user102008
@user102008 我试过了,但它无法工作。错误的图像已经排队在主线程中(refresh正在执行过程中),等待显示。 - Nimit Pattanasri
1个回答

1

好的... 另一种方法是将您的“图像请求代码”放入AnimatedImageView中,并在每次设置新的Image URL时使挂起的请求无效。

// your controller
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    ...
    [cell.imageView setDistantImage:imageURL];
    ...    
    }

然后,在您的Cell类中,“setDistantImage”创建一个ASIHttpRequest,并使之前的请求无效。
//your ImageView class

@property (...) ASIHttpRequest *distantImageRequest;

- (void) setDistantImageUrl:(NSUrl *)imageUrl
{
    //clear previous request
    [distantImageRequest clearDelegatesAndCancel];
    //set a new request, with the callBack embedded directly in this ImageView class
    ... 
}

//animation and callBacks methods in this ImageView class too...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接