iOS CollectionView:异步加载数据

时间:2014-08-27 06:36:23

标签: ios asynchronous uicollectionview

我正在使用集合视图并尝试从同步加载数据转换为异步加载它。

我知道以下内容当前有效(加载需要一段时间,但所有单元格在完成时都会正确显示):

// load projectData in main thread
NSData * projectData = [NSData dataWithContentsOfURL:userUrl];
[self performSelectorOnMainThread:@selector(fetchProjects:)withObject:projectData waitUntilDone:YES];

我重写了它以异步方式完成所有事情:

// load project data asynchronously
 dispatch_async(bgQueue, ^{
     UIView *loadingAnimation = loadingCircle;
     loadingAnimation.tag = 15;
     [self.collectionView addSubview:loadingAnimation];
     [loadingCircle startAnimating];

     NSData * projectData = [NSData dataWithContentsOfURL:userUrl];

     [self performSelector:@selector(fetchProjects:) withObject:projectData];

     dispatch_async(dispatch_get_main_queue(), ^{
         NSLog(@"finished with loading projects");
         UIView *viewToRemove = [self.view viewWithTag:15];
         [viewToRemove removeFromSuperview];
         [self.collectionView reloadData];
     });
 });

当我在异步加载数据后运行应用程序时,视图显示为空(单元格没有内容),但是当我滚动时,一些单元格开始出现。

除了reloadData之外还有什么我需要调用才能使我的集合单元格正确显示?

这是我的fetchProjects:

// get JSON data of projects
- (void)fetchProjects:(NSData *)responseData {
    NSError * error;
    NSDictionary * json = [NSJSONSerialization
                           JSONObjectWithData:responseData
                           options:kNilOptions
                           error:&error]; // get dictionary from json data
    NSDictionary * data = [json objectForKey:@"data"]; // get data in array
    NSArray * projects = [data objectForKey:@"projects"];
    NSDictionary * mostRecentProject = [projects objectAtIndex:0];

    mostRecentProjectID = [mostRecentProject objectForKey:@"id"];

    for (NSDictionary *currentProject in projects)
    {
        [projectIDs addObject: [currentProject objectForKey:@"id"]];
        NSString *projectTitle = [currentProject objectForKey:@"title"];
        NSString *trimmedProjectTitle = [projectTitle stringByTrimmingCharactersInSet:
                                         [NSCharacterSet whitespaceAndNewlineCharacterSet]];
        id delegate = [[UIApplication sharedApplication] delegate];
        self.managedObjectContext = [delegate managedObjectContext];
        Project *newProject = (Project *) [NSEntityDescription insertNewObjectForEntityForName:@"Project" inManagedObjectContext:[self managedObjectContext]];

        CustomLabel *cellLabel=[[CustomLabel alloc]init];
        cellLabel.text = trimmedProjectTitle;
        NSLog(@"fetchprojects:%@",projectTitle);
        [titles addObject:projectTitle];

        CGSize maxLabelSize = CGSizeMake(screenWidth/2 - 30,100);

        CustomLabel *titleLabel = [[CustomLabel alloc]init];
        [titleLabel setNumberOfLines:0];
        titleLabel.text = projectTitle;

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

        CGRect labelFrame = (CGRectMake(0, 0, screenWidth/2 - 30, 0));
        labelFrame.origin.x = 0;
        labelFrame.origin.y = screenWidth/2 - 70 - expectedLabelSize.height;
        labelFrame.size.height = expectedLabelSize.height;
        titleLabel.frame = labelFrame;

        titleLabel.backgroundColor = [[UIColor blackColor]colorWithAlphaComponent:0.5f];
        titleLabel.textColor =[UIColor whiteColor];
        [titleLabel setFont: [UIFont fontWithName: @"HelveticaNeue" size:12]];
        //NSLog(@"%@", titleLabel.text);

        UIImageView *imagePreview = [[UIImageView alloc] initWithFrame:CGRectMake(7.5, 10, screenWidth/2 -30, screenWidth/2 -70)];
        imagePreview.contentMode= UIViewContentModeScaleAspectFill;
        imagePreview.clipsToBounds = YES;
        [imagePreview setImage:[UIImage imageNamed:@"blank.png"]];
        [imagePreview addSubview:titleLabel];
        [imagePreview.subviews[0] setClipsToBounds:YES];
        [projectContainers addObject: imagePreview];
}
}

1 个答案:

答案 0 :(得分:2)

你在后台线程上做了很多UI工作,你真的不应该这样做。从我所看到的,真正需要在后台线程上运行的唯一一行就是这一行:

NSData * projectData = [NSData dataWithContentsOfURL:userUrl];

其余部分看起来像处理设置和显示你的UI和一些CoreData的东西; 所有这些都需要在主线程上运行。最简单的方法是保持一切按正确的顺序运行:

// NOTE: If you're sure you're already on the main thread here, you don't need the dispatch, but it's not going to hurt to leave it in.
dispatch_async(dispatch_get_main_queue(), ^{
    UIView *loadingAnimation = loadingCircle;
    loadingAnimation.tag = 15;
    [self.collectionView addSubview:loadingAnimation];
    [loadingCircle startAnimating];
});

dispatch_async(bgQueue, ^{
    NSData * projectData = [NSData dataWithContentsOfURL:userUrl];

    dispatch_async(dispatch_get_main_queue(), ^{
        [self fetchProjects:projectData];

        NSLog(@"finished with loading projects");
        UIView *viewToRemove = [self.view viewWithTag:15];
        [viewToRemove removeFromSuperview];
        [self.collectionView reloadData];
    });
});

注意:我还将[self performSelector:@selector(fetchProjects:) withObject:projectData]更改为[self fetchProjects:projectData];你真的不需要经过那里的performSelector: