【问题标题】:iOS: How to stop a background thread from updating UI in the main thread?iOS:如何阻止后台线程在主线程中更新 UI?
【发布时间】:2012-07-21 13:11:30
【问题描述】:

我实现了一个 UIImageView 单元的 UITableView,每个单元通过 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) {
    }];
}

【问题讨论】:

  • WWDC 2012 会议 211 详细介绍了这一点,包括不同级别的队列取消、优化等。这是一个很棒的视频。
  • cellForRow 中,为什么不立即将图像设置为零呢?这样一来,它将暂时空白而不是错误的图像。
  • @user102008 我这样做了,但它不起作用。错误的图像已经在主线程中排队(refresh 正在执行中),等待显示。

标签: ios multithreading uitableview uiimageview lazy-loading


【解决方案1】:

好的... 另一种方法是将“图像请求代码”放入 AnimatedImageView,并在每次设置新图像 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...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-13
    • 2012-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-17
    • 2015-03-20
    • 1970-01-01
    相关资源
    最近更新 更多