使用dispatch_async控制UICollectionView单元的加载顺序

时间:2014-09-07 15:29:14

标签: ios objective-c asynchronous uicollectionview

在我的iOS应用程序中,我有一个UICollectionView,其中每个单元格都包含一个图像。为了防止视图花费很长时间加载,我在加载背景任务中的图像内容之前加载每个空白图像和标题。

我在后台异步任务中记录了哪些图像正在加载,看起来屏幕上的单元格图像首先加载,然后是屏幕顶部的单元格。这使得应用程序似乎没有响应,我宁愿让顶部的单元格在加载方面具有优先权:

enter image description here

我还注意到,一旦我开始滚动,单元格中的图像突然开始出现,但它们需要更长时间才能显示出来。 有人可以提出控制UICollectionCell加载顺序的策略吗?

这是我的代码:

迭代项目并将imageView添加到NSMutableArray projectContainers,然后将其转换为单元格

for (NSDictionary *currentProject in projects)
    {
        // data entry
        [projectIDs addObject: [currentProject objectForKey:@"id"]];
        NSString *projectTitle = [currentProject objectForKey:@"title"];

        id delegate = [[UIApplication sharedApplication] delegate];
        self.managedObjectContext = [delegate managedObjectContext];

        CustomLabel *cellLabel=[[CustomLabel alloc]init];
        cellLabel.text = trimmedProjectTitle;
        [titles addObject:projectTitle];

        CGSize maxLabelSize = CGSizeMake(cellWidth,100);

        CustomLabel *titleLabel = [[CustomLabel alloc]init]; 
        // titleLabel styling
        titleLabel.backgroundColor = [[UIColor blackColor]colorWithAlphaComponent:0.5f];
        titleLabel.textColor =[UIColor whiteColor];
        [titleLabel setFont: [UIFont fontWithName: @"HelveticaNeue" size:12]];

        titleLabel.text = trimmedProjectTitle;
        CGSize expectedLabelSize = [titleLabel.text sizeWithFont:titleLabel.font constrainedToSize:maxLabelSize lineBreakMode:NSLineBreakByWordWrapping];

        CGRect labelFrame = (CGRectMake(0, 0, cellWidth, 0));
        labelFrame.origin.x = 0;
        labelFrame.origin.y = screenWidth/2 - 80 - expectedLabelSize.height;
        labelFrame.size.height = expectedLabelSize.height+10;
        titleLabel.frame = labelFrame;

        // add placeholder image with textlabel
        UIImageView *imagePreview = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, cellWidth, cellHeight)];
        imagePreview.contentMode= UIViewContentModeScaleAspectFill;
        imagePreview.clipsToBounds = YES;
        [imagePreview setImage:[UIImage imageNamed:@"blank.png"]];
        [imagePreview addSubview:titleLabel];
        [imagePreview.subviews[0] setClipsToBounds:YES];
        [projectContainers addObject: imagePreview];

        // add project thumbnail images in async
        dispatch_async(bgQueue, ^{
          NSDictionary *imagePath = [currentProject objectForKey:@"image_path"];
          NSString *imageUrlString = [imagePath objectForKey: @"preview"];
          NSURL *imageUrl = [NSURL URLWithString: imageUrlString];
          NSData *imageData = [[NSData alloc] initWithContentsOfURL:(imageUrl)];
          UIImage *image = [[UIImage alloc] initWithData:(imageData)];
          if(image){
              NSLog(@"project with image: %@", projectTitle);
              [imagePreview setImage: image];
          }
            BOOL *builtVal = [[currentProject objectForKey:@"built"]boolValue];
            if(builtVal){
                UIImageView *builtBanner =[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"built_icon.png"]];
                builtBanner.frame = CGRectMake(screenWidth/2 -80, 0, 50, 50);
                [imagePreview addSubview: builtBanner];
            }
        });
    }

使用NSMutableArray projectContainers渲染单元格:

-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
//    NSLog(@"cellForItemAtIndexPath");
        static NSString *identifier = @"NewCell";
        UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
        if(!reloadProjects){
            UIImageView *preview = (UIImageView*) [cell.contentView viewWithTag:[[projectIDs objectAtIndex:indexPath.row]intValue]];
            UIImageView *previewContent = [projectContainers objectAtIndex:indexPath.row];
            //    NSLog(@"fetching image tag %d", [[projectIDs objectAtIndex:indexPath.row]intValue]);

            if (!preview)
            {
                previewContent.tag = [[projectIDs objectAtIndex:indexPath.row]intValue];
                //        NSLog(@"creating previewContent %li", (long) previewContent.tag);
                [cell addSubview: previewContent];
            }

            [self.collectionView setBackgroundColor:collectionGrey];
            cell.contentView.layer.backgroundColor  = [UIColor whiteColor].CGColor;

            return cell;
        }
    return cell;
}

编辑:工作解决方案

感谢rob mayoff帮助我提出解决方案。这就是我最终做的事情,它可以更快地加载图像:

// add project thumbnail images in async
        dispatch_async(imageQueue, ^{
          NSDictionary *imagePath = [currentProject objectForKey:@"image_path"];
          NSString *imageUrlString = [imagePath objectForKey: @"preview"];
          NSURL *imageUrl = [NSURL URLWithString: imageUrlString];
          NSData *imageData = [[NSData alloc] initWithContentsOfURL:(imageUrl)];
          UIImage *image = [[UIImage alloc] initWithData:(imageData)];
          if(image){
              dispatch_async(dispatch_get_main_queue(), ^{
                    NSLog(@"project with image: %@", projectTitle);
                    [imagePreview setImage: image];
              });
          }
            BOOL *builtVal = [[currentProject objectForKey:@"built"]boolValue];
            if(builtVal){
                UIImageView *builtBanner =[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"built_icon.png"]];
                builtBanner.frame = CGRectMake(screenWidth/2 -80, 0, 50, 50);
                dispatch_async(dispatch_get_main_queue(), ^{
                    [imagePreview addSubview: builtBanner];
                });
            }
        });

1 个答案:

答案 0 :(得分:2)

在您的代码中有一些代码需要改进的东西,但是您的主要抱怨(“一旦我开始滚动,单元格中的图像突然开始出现,但它们需要更长时间才能自己出现”)是因为你违反了诫命:

您只能访问

您的视图层次结构

来自主线程。

看看你的代码:

    dispatch_async(bgQueue, ^{
        ...
            [imagePreview addSubview: builtBanner];

您正在从后台线程操纵视图层次结构。这是不允许的。例如,请参阅this page底部的注释或“Threading Considerations” in the UIView Class Reference

您需要调度回主线程以更新视图层次结构。

观看Session 211 - Building Concurrent User Interfaces on iOS video from WWDC 2012。它深入探讨了如何有效地做你正在努力做的事情。另请参阅this answer