Reputation: 9415
I'm using a collection view and trying to transition from loading the data synchronously to loading it asynchronously.
I know that the following currently works (it takes a while to load, but all the cells appear correctly when it's done):
// load projectData in main thread
NSData * projectData = [NSData dataWithContentsOfURL:userUrl];
[self performSelectorOnMainThread:@selector(fetchProjects:)withObject:projectData waitUntilDone:YES];
I rewrote it to do everything asynchronously:
// 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];
});
});
When I run the app after loading the data asynchronously, the view appears empty (the cells have no content), but when I scroll, some of the cells begin to appear.
Is there anything else I need to call besides reloadData to get my collection cells to appear properly?
Here is my 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];
}
}
Upvotes: 1
Views: 1844
Reputation: 42325
You're doing a lot of UI work on a background thread which you really shouldn't do. From what I can see, the only line that really needs to be run on a background thread is this one:
NSData * projectData = [NSData dataWithContentsOfURL:userUrl];
The rest looks like it deals with setting up and displaying your UI and some CoreData stuff; all of that needs to be run on the main thread. The easiest way to do that and keep everything running in the right order would be something like this:
// 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];
});
});
Note: I also changed [self performSelector:@selector(fetchProjects:) withObject:projectData]
to [self fetchProjects:projectData]
; you don't really need to go through performSelector:
there.
Upvotes: 2