I use code as below to download images, I found there is problem but I am not sure where is, can you give some sueesgtion?You can keep a count (ivar/property) of the total images downloaded. If a download block success, you increment the count. Then you check if the count equals the total cells in your table. If, yes, you hide the indicator.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"searchResultCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if (self.cachesIconImage == nil) {
self.cachesIconImage = [[NSMutableDictionary alloc] initWithCapacity:10];
}
cell.textLabel.text = [self.searchResultsTrackName objectAtIndex:indexPath.row];
cell.imageView.image = [self.cachesIconImage objectForKey:[NSNumber numberWithInt:indexPath.row]];
if (!cell.imageView.image) {
cell.imageView.image = [UIImage imageNamed:@"Icon.png"];
dispatch_async(kBgQueue, ^{
NSString *iconURL = [self.searchResultsIconURL objectAtIndex:indexPath.row];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:iconURL]];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *icon = [UIImage imageWithData:imageData];
[self.cachesIconImage setObject:icon forKey:[NSNumber numberWithInt:indexPath.row]];
cell.imageView.image = icon;
});
});
}
[cell setNeedsLayout];
return cell;
}
- (void)fetchedData:(NSData *)responseData {
NSError *error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
self.searchResults = [json objectForKey:@"results"];
self.searchResultsTrackName = [self.searchResults valueForKey:@"trackName"];
self.searchResultsIconURL = [self.searchResults valueForKey:@"artworkUrl60"];
[self.searchTableView reloadData];
[self downloadImages];
}
- (void)downloadImages {
if (self.cachesIconImage == nil) {
self.cachesIconImage = [[NSMutableDictionary alloc] initWithCapacity:10];
}
NSUInteger count = self.searchResultsIconURL.count;
for (NSUInteger index = 0; index < count; index++) {
dispatch_async(kBgQueue, ^{
NSString *iconURL = [self.searchResultsIconURL objectAtIndex:index];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:iconURL]];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *icon = [UIImage imageWithData:imageData];
[self.cachesIconImage setObject:icon forKey:[NSNumber numberWithInt:index]];
});
});
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
- (void)downloadImages {
if (self.cachesIconImage == nil) {
self.cachesIconImage = [[NSMutableDictionary alloc] initWithCapacity:10];
}
NSUInteger count = self.searchResultsIconURL.count;
for (NSUInteger index = 0; index < count; index++) {
static int i = 0;
dispatch_async(kBgQueue, ^{
NSString *iconURL = [self.searchResultsIconURL objectAtIndex:index];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:iconURL]];
i++;
if (i == count - 1) {
dispatch_async(dispatch_get_main_queue(), ^{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
});
}
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *icon = [UIImage imageWithData:imageData];
[self.cachesIconImage setObject:icon forKey:[NSNumber numberWithInt:index]];
});
});
}
}
Do you think downloadImages and fetchData methods are no problems? I found sometimes downloadImges doesn't work, especially in 2nd time to run it! Does async need to be released? or any bug else? I am not sure about blocks...Try this in your download method:
Code:- (void)downloadImages { if (self.cachesIconImage == nil) { self.cachesIconImage = [[NSMutableDictionary alloc] initWithCapacity:10]; } NSUInteger count = self.searchResultsIconURL.count; for (NSUInteger index = 0; index < count; index++) { dispatch_async(kBgQueue, ^{ NSString *iconURL = [self.searchResultsIconURL objectAtIndex:index]; NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:iconURL]]; if (index == count - 1) { dispatch_async(dispatch_get_main_queue(), ^{ [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; } } dispatch_async(dispatch_get_main_queue(), ^{ UIImage *icon = [UIImage imageWithData:imageData]; [self.cachesIconImage setObject:icon forKey:[NSNumber numberWithInt:index]]; }); }); } }
At first, I just download in cellForRow, then I used downloadImages to cache them, but I thought it might be possible when a cell is displayed but that image was not yet downloaded so I keep downloading in cellForRow, is it right?Well, they look fine, but I do not understand why you are again downloading the image in cellForRowAtIndexPath
That is what I did in cellForRow, I only download image that is not yet downloaded (not yet cached).
I tried you code, but seems like the indicator hide before all images downloaded, what do you think about this?Try this in your download method:
Code:- (void)downloadImages { if (self.cachesIconImage == nil) { self.cachesIconImage = [[NSMutableDictionary alloc] initWithCapacity:10]; } NSUInteger count = self.searchResultsIconURL.count; for (NSUInteger index = 0; index < count; index++) { static int i = 0; dispatch_async(kBgQueue, ^{ NSString *iconURL = [self.searchResultsIconURL objectAtIndex:index]; NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:iconURL]]; i++; if (i == count - 1) { dispatch_async(dispatch_get_main_queue(), ^{ [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; }); } dispatch_async(dispatch_get_main_queue(), ^{ UIImage *icon = [UIImage imageWithData:imageData]; [self.cachesIconImage setObject:icon forKey:[NSNumber numberWithInt:index]]; }); }); } }
- (void)downloadImages {
if (self.cachesIconImage == nil) {
self.cachesIconImage = [[NSMutableDictionary alloc] initWithCapacity:10];
}
NSUInteger count = self.searchResultsIconURL.count;
[B]__block NSUInteger downloaded = 0;[/B]
for (NSUInteger index = 0; index < count; index++) {
if ([self.cachesIconImage objectForKey:[NSNumber numberWithInt:index]] == nil) {
dispatch_async(kBgQueue, ^{
NSString *iconURL = [self.searchResultsIconURL objectAtIndex:index];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:iconURL]];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *icon = [UIImage imageWithData:imageData];
[self.cachesIconImage setObject:icon forKey:[NSNumber numberWithInt:index]];
[b]if (downloaded == count - 1) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
downloaded++;[/b]
});
});
}
}
}
I change your code as below, now it does work in Simulator, but when I tried it on iPhone4S, downloadImages seems like not be executed, do you know why?
Code:- (void)downloadImages { if (self.cachesIconImage == nil) { self.cachesIconImage = [[NSMutableDictionary alloc] initWithCapacity:10]; } NSUInteger count = self.searchResultsIconURL.count; [B]__block NSUInteger downloaded = 0;[/B] for (NSUInteger index = 0; index < count; index++) { if ([self.cachesIconImage objectForKey:[NSNumber numberWithInt:index]] == nil) { dispatch_async(kBgQueue, ^{ NSString *iconURL = [self.searchResultsIconURL objectAtIndex:index]; NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:iconURL]]; dispatch_async(dispatch_get_main_queue(), ^{ UIImage *icon = [UIImage imageWithData:imageData]; [self.cachesIconImage setObject:icon forKey:[NSNumber numberWithInt:index]]; [b]if (downloaded == count - 1) { [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; } downloaded++;[/b] }); }); } } }
I just test, downloadImages is executed, but code as below not be run, I don't know why, maybe due to blocks..Strange, have you checked it by placing a breakpoint in downloadImages: to see if it is ever being called? Also, the use of block variable looks correct to me, although I haven't played with blocks for a while.
dispatch_async(kBgQueue, ^{
}];
I just test, downloadImages is executed, but code as below not be run, I don't know why, maybe due to blocks..
Code:dispatch_async(kBgQueue, ^{ }];
- (void)downloadImages {
if (self.cachesIconImage == nil) {
self.cachesIconImage = [[NSMutableDictionary alloc] initWithCapacity:10];
}
NSUInteger count = self.searchResultsIconURL.count;
__block NSUInteger downloaded = 0;
__block blockSafeSelf = self;
for (NSUInteger index = 0; index < count; index++) {
if ([self.cachesIconImage objectForKey:[NSNumber numberWithInt:index]] == nil) {
dispatch_async(kBgQueue, ^{
NSString *iconURL = [blockSafeSelf.searchResultsIconURL objectAtIndex:index];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:iconURL]];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *icon = [UIImage imageWithData:imageData];
[blockSafeSelf.cachesIconImage setObject:icon forKey:[NSNumber numberWithInt:index]];
if (downloaded == count - 1) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
downloaded++;
});
});
}
}
}
Your code doesn't work, I got the same result as before....Does this work?
Code:- (void)downloadImages { if (self.cachesIconImage == nil) { self.cachesIconImage = [[NSMutableDictionary alloc] initWithCapacity:10]; } NSUInteger count = self.searchResultsIconURL.count; __block NSUInteger downloaded = 0; __block blockSafeSelf = self; for (NSUInteger index = 0; index < count; index++) { if ([self.cachesIconImage objectForKey:[NSNumber numberWithInt:index]] == nil) { dispatch_async(kBgQueue, ^{ NSString *iconURL = [blockSafeSelf.searchResultsIconURL objectAtIndex:index]; NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:iconURL]]; dispatch_async(dispatch_get_main_queue(), ^{ UIImage *icon = [UIImage imageWithData:imageData]; [blockSafeSelf.cachesIconImage setObject:icon forKey:[NSNumber numberWithInt:index]]; if (downloaded == count - 1) { [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; } downloaded++; }); }); } } }
Your code doesn't work, I got the same result as before....
[self.cachesIconImage objectForKey:[NSNumber numberWithInt:index]]
I tried code as below, almost time "Finished download" in thread main not displayed, but some time (very rarely) is displayed.This is very weird. I'm sorry to ask again, but if you add a breakpoint __inside__ the block of the dispatch function, it never halts there?
Also, are you sure that this returns nil?
Code:[self.cachesIconImage objectForKey:[NSNumber numberWithInt:index]]
Also, can you try running both blocks on the same (main) thread, instead of a background queue?
- (void)downloadImages {
if (cachesIconImage_ == nil) {
self.cachesIconImage = [[NSMutableDictionary alloc] initWithCapacity:10];
}
for (NSUInteger index = 0; index < countImages; index++) {
if ([self.cachesIconImage objectForKey:[NSNumber numberWithInt:index]] == nil) {
NSLog(@"Sartt download %i", index);
dispatch_async(kBgQueue, ^{
NSString *iconURL = [self.searchResultsIconURL objectAtIndex:index];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:iconURL]];
NSLog(@"icon url= %@", iconURL);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Finished download");
UIImage *icon = [UIImage imageWithData:imageData];
[self.cachesIconImage setObject:icon forKey:[NSNumber numberWithInt:index]];
});
});
}
}
}