iOS版:如何从主线程更新UI停止后台线程?(iOS: How to stop a backgrou

2019-09-20 01:53发布

我实现的UIImageView细胞,其中的每一个通过的NSTimer定期刷新本身每5秒的一个UITableView。 每个图像从后台线程服务器加载,并从后台线程我也更新UI,显示新的图像,通过调用performSelectorOnMainThread 。 到现在为止还挺好。

目前,我面临着一个问题,当我不用等到图像是在当前小区加载,并且我迅速向下滚动到新的小区。 这种新的细胞,但是,显示先前的小区,而不是新的细胞的图像。 虽然下一轮的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) {
    }];
}

Answer 1:

好吧...另一种方式做到这一点是把你的形象请求代码“到你的AnimatedImageView,并在每次设置新的图像URL时无效挂起的请求

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

然后,在你的手机类,“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...


Answer 2:

载入中的CollectionView和TableView中的许多图像与重用的小区覆盖所有具有快速和长期滚动下载的图像时,我有完全相同的问题。

对于TableView中,我解决了这个问题看,其中提出了“didEndDisplayingCell”方法的WWDC 2012,但是这不是有用对于他没有这种方法的CollectionView。 :O(

最后,我发现了两个解决方案...还好。 我们的想法是创建一个类ImageLoader的从TableView中或在的CollectionView细胞叫,这将是负责下载图像 。 在您的实现,添加这段代码隐藏变量到世界其他地区:

@interface ImageLoader() {
__block NSURLSession *_session;
}

然后,添加以下功能的实现:

- (void)getViewForCell:(UIImageView*)cell
               FromURL:(NSURL*)url {

NSBlockOperation *_loadImageOp;
__block NSOperationQueue *_opQueue;

_opQueue = [[NSOperationQueue alloc]init];

if (_session != nil) {
    [_session invalidateAndCancel];
}

_loadImageOp = [[NSBlockOperation alloc]init];
[_loadImageOp addExecutionBlock:^{

    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    _session = [NSURLSession sessionWithConfiguration:config
                                             delegate:nil
                                        delegateQueue:_opQueue];

    NSURLSessionDownloadTask *downloadTask = [_session downloadTaskWithURL:url
                                                         completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
        UIImage *imageCell = [UIImage imageWithData:[NSData dataWithContentsOfURL:location]];

        if (imageCell) {
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                cell.image = imageCell;
            }];
        } else {
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                cell.image = [UIImage imageNamed:@"imageNotWorking.png"];
            }];
        }
    }];

    [downloadTask resume];
}];

[_opQueue addOperation:_loadImageOp];
}

这个想法是,1 /。 创建的块,其中所述成员变量_session将获得经由downloadTask,2 /传入参数中给定的URL。 你时,它的下载... 3 /通过mainQueue显示图像用户界面。 初始创建的块被添加到专用于该特定重用信元队列。

最重要的是“invalidateAndCancel”功能,因为你想重用小区内的每个时间,前者会不会把你带回来的最后一个图像,而是由传入的参数“定义新建一个名为如果成员变量不为零网址”。

之后,不要忘了把1 /。以下片段在你的TableView /的CollectionView细胞类以只使用一个ImageLoader的每一个复用小区和2 /。该方法在方法被调用“的tableView:的cellForRowAtIndexPath:”您的CollectionView / TableView中的:

@interface TableCell () { //...or CollectionCell  :o)
ImageLoader *_imgLoader;
}
@end

@implementation TableCell
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
_imgLoader = [[ImageLoader alloc]init];
return self;
}

- (void)imageURL:(NSURL*)url {
[_imgLoader getViewForCell:self.imageViewWhereTheDownloadedImageIs
                   FromURL:url];
}
@end

一旦完成,只需调用[cell imageURL:url]的方法'的tableView:的cellForRowAtIndexPath:'你的CollectionView / TableView中的。

现在,你可以如你所愿的快速滚动,你就会有永远正确的形象,只有这一个。

我希望这将帮助......很好。 :O)



文章来源: iOS: How to stop a background thread from updating UI in the main thread?