I've got a UITableView that loads an image asynchronously and places it in the UITableViewCell once it's loaded (I'm using almost the exact same code as in the "LazyTableImages" tutorial). This works fine for all images when I scroll the table, but it's not loading the images that are first in the view.
The code is definitely working fine as the class that actually sends the NSURLConnection request is being called correctly (I added an NSLog and it reached the console). The NSURLConnection is just not calling the delegate methods (didReceiveData, connectionDidFinishLoading, etc).
Here's my code:
HomeController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
NSArray *feed = [feeds objectAtIndex: indexPath.row];
/**
* Name of person
*/
[...]
/**
* Feed entry
*/
[...]
/**
* Misc work
*/
[...]
}
FeedRecord *feedRecord = [self.entries objectAtIndex:indexPath.row];
if( !feedRecord.image ) {
if (self.table.dragging == NO && self.table.decelerating == NO)
{
[self startIconDownload:feedRecord forIndexPath:indexPath];
}
cell.imageView.image = [UIImage imageNamed:@"Placeholder.png"];
}
return cell;
}
- (void)startIconDownload:(FeedRecord *)feedRecord forIndexPath:(NSIndexPath *)indexPath
{
IconDownloader *iconDownloader = [imageDownloadsInProgress objectForKey:indexPath];
if (iconDownloader == nil)
{
iconDownloader = [[IconDownloader alloc] init];
iconDownloader.feedRecord = feedRecord;
iconDownloader.indexPathInTableView = indexPath;
iconDownloader.delegate = self;
[imageDownloadsInProgress setObject:iconDownloader forKey:indexPath];
[iconDownloader startDownload];
[iconDownloader release];
}
}
IconDownload.m
#import "IconDownloader.h"
#import "FeedRecord.h"
#define kAppIconHeight 48
@implementation IconDownloader
@synthesize feedRecord;
@synthesize indexPathInTableView;
@synthesize delegate;
@synthesize activeDownload;
@synthesize imageConnection;
#pragma mark
- (void)dealloc
{
[feedRecord release];
[indexPathInTableView release];
[activeDownload release];
[imageConnection cancel];
[imageConnection release];
[super dealloc];
}
- (void)startDownload
{
NSLog(@"%@ %@",@"Started downloading", feedRecord.profilePicture); // this shows in log
self.activeDownload = [NSMutableData data];
// alloc+init a开发者_运维百科nd start an NSURLConnection; release on completion/failure
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:
[NSURLRequest requestWithURL:
[NSURL URLWithString:feedRecord.profilePicture]] delegate:self];
self.imageConnection = conn;
NSLog(@"%@",conn); // this shows in log
[conn release];
}
- (void)cancelDownload
{
[self.imageConnection cancel];
self.imageConnection = nil;
self.activeDownload = nil;
}
#pragma mark -
#pragma mark Download support (NSURLConnectionDelegate)
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(@"%@ %@",@"Got data for", feedRecord.profilePicture);
[self.activeDownload appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(@"%@",@"Fail!");
// Clear the activeDownload property to allow later attempts
self.activeDownload = nil;
// Release the connection now that it's finished
self.imageConnection = nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(@"%@ %@",@"Done", feedRecord.profilePicture);
// Set appIcon and clear temporary data/image
UIImage *image = [[UIImage alloc] initWithData:self.activeDownload];
self.feedRecord.image = image;
self.activeDownload = nil;
[image release];
// Release the connection now that it's finished
self.imageConnection = nil;
NSLog(@"%@ %@",@"Our delegate is",delegate);
// call our delegate and tell it that our icon is ready for display
[delegate feedImageDidLoad:self.indexPathInTableView];
}
@end
Has anyone else experienced anything like this or can identify an issue with my code? Thanks!
you can use this code
[tableView performSelector:@selector(reloadData) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES];
instead
[tableView reloadData];
You don't call the start method of the NSURLConnection object you create in your startDownload method.
Be sure to do it :
- (void)startDownload
{
NSLog(@"%@ %@",@"Started downloading", feedRecord.profilePicture); // this shows in log
self.activeDownload = [NSMutableData data];
// alloc+init and start an NSURLConnection; release on completion/failure
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:
[NSURLRequest requestWithURL:
[NSURL URLWithString:feedRecord.profilePicture]] delegate:self];
self.imageConnection = conn;
NSLog(@"%@",conn); // this shows in log
[conn start];
[conn release];
}
You can also use the constructor : initWithRequest:delegate:startImmediately:
Also, your download will be blocked because of the run loop they are running if the user scrolls. Simply register your connection in the "common modes" :
[conn scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
Extracted from : how-to-avoid-blocked-downloads-during-scrolling
You do not start our NSURLConnection
. Either initialize it with -[NSURLConnection initWithRequest:delegate:startImmediately:]
or manually call -[NSURLConnection start]
after initialization.
I have the same issue. Also, I nearly use the same code as you (it is from Apple sample LazyTableImages).
While the code in Apple's test project works, it did not work in my project - although I just made a copy of Apple's code.
What I found is: When I used
NSLog(@"Is%@ main thread", ([NSThread isMainThread] ? @"" : @" NOT"));
in cellForRowAtIndexPath: as well as IconDownload.m's startDownload: (in both projects), I found out that it is the main thread in Apple's sample, but NOT main thread in my code.
This might be the problem.
Any idea how to solve?
EDIT !!! Solved !
I just forced main thread using
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:entry.imageURL, @"imageURL", indexPath, @"indexPath", nil];
[self performSelectorOnMainThread:@selector(startIconDownload:) withObject:info waitUntilDone:NO];
in cellForRowAtIndexPath: You will need a dictionary to send more than one argument to the method.
You can do a similar solution in your code. Replace the line:
[self startIconDownload:feedRecord forIndexPath:indexPath];
with my code and modify startIconDownload: like this
- (void)startIconDownload:(NSDictionary *)info
{
NSString *url = [info objectForKey:@"imageURL"];
NSIndexPath *indexPath = [info objectForKey:@"indexPath"];
...
}
Some variables my be different in your app.
But I just can't understand why it works in Apple's sample without forcing main thread.
Any idea?
Have a look here: http://www.depl0y.com/?p=345 Maybe will help.
Edit: Yep, is working for me. Let me know if is working for you too or you need more information.
精彩评论