NSURLConnection 在 UITableView 滚动之前不会“触发”

Posted

技术标签:

【中文标题】NSURLConnection 在 UITableView 滚动之前不会“触发”【英文标题】:NSURLConnection not "firing" until UITableView scrolls 【发布时间】:2010-06-09 06:19:59 【问题描述】:

我有一个 UITableView,它异步加载图像并在加载后将其放置在 UITableViewCell 中(我使用的代码与“LazyTableImages”教程中的代码几乎完全相同)。当我滚动表格时,这适用于所有图像,但它不会加载视图中的第一个图像。

代码确实可以正常工作,因为正确调用了实际发送 NSURLConnection 请求的类(我添加了一个 NSLog 并到达了控制台)。 NSURLConnection 只是不调用委托方法(didReceiveData、connectionDidFinishLoading 等)。

这是我的代码:


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 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 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

是否有其他人遇到过类似的情况或可以识别我的代码存在问题?谢谢!

【问题讨论】:

【参考方案1】:

您可以使用此代码

[tableView performSelector:@selector(reloadData) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES];

改为

[tableView reloadData];

【讨论】:

【参考方案2】:

您不会调用您在 startDownload 方法中创建的 NSURLConnection 对象的 start 方法。

一定要这样做:

- (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];

你也可以使用构造函数:initWithRequest:delegate:startImmediately:

此外,如果用户滚动,您的下载将被阻止,因为它们正在运行的运行循环。只需在“常用模式”中注册您的连接:

[conn scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

摘自:how-to-avoid-blocked-downloads-during-scrolling

【讨论】:

【参考方案3】:

您没有启动我们的NSURLConnection。要么用-[NSURLConnection initWithRequest:delegate:startImmediately:]初始化它,要么在初始化后手动调用-[NSURLConnection start]

【讨论】:

不。我得到的代码将初始化请求并立即开始下载 URL。 (Apple 的方法文档:“返回一个初始化的 URL 连接并开始加载 URL 请求的数据。”)。 [NSURLConnection start] 也不起作用:( 很抱歉我误读了文档。实际上,我看不到您的代码有问题。是否在 startDownload 方法中设置了断点并检查是否一切都如预期的那样?【参考方案4】:

我也有同样的问题。此外,我几乎使用与您相同的代码(来自 Apple 示例 LazyTableImages)。

虽然 Apple 的测试项目中的代码有效,但它在我的项目中不起作用 - 尽管我只是复制了 Apple 的代码。

我发现的是:我用的时候

NSLog(@"Is%@ main thread", ([NSThread isMainThread] ? @"" : @" NOT"));

在 cellForRowAtIndexPath: 以及 IconDownload.m 的 startDownload: 中(在两个项目中),我发现它是 Apple 示例中的主线程,但在我的代码中 NOT 主线程。 p>

这可能是问题所在。

知道怎么解决吗?


编辑!!!解决了!

我只是强制使用主线程

NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:entry.imageURL, @"imageURL", indexPath, @"indexPath", nil];
[self performSelectorOnMainThread:@selector(startIconDownload:) withObject:info waitUntilDone:NO];

在 cellForRowAtIndexPath 中:您需要一个字典来向该方法发送多个参数。

您可以在代码中执行类似的解决方案。换行:

[self startIconDownload:feedRecord forIndexPath:indexPath];

用我的代码修改 startIconDownload: 像这样

- (void)startIconDownload:(NSDictionary *)info

    NSString *url = [info objectForKey:@"imageURL"];
    NSIndexPath *indexPath = [info objectForKey:@"indexPath"];
    ...

您的应用中的某些变量可能会有所不同。

但我只是不明白为什么它在不强制主线程的情况下在 Apple 的示例中工作。

有什么想法吗?

【讨论】:

【参考方案5】:

看看这里:http://www.depl0y.com/?p=345 也许会有所帮助。

编辑:是的,正在为我工​​作。让我知道是否也适合您,或者您需要更多信息。

【讨论】:

以上是关于NSURLConnection 在 UITableView 滚动之前不会“触发”的主要内容,如果未能解决你的问题,请参考以下文章

uisearchbar 在分组部分 uitable

如何将文本包装在 Iphone 的 UITable 单元格中?

操作按钮在 UITable 中不起作用

关于 UITable 视图的异常

如何在 UITable 中实现 Instagram 评论的效果?

当用户选择 UITable 行时显示等待屏幕