Objective-C:异步填充UITableView - 如何做到这一点?

时间:2011-09-20 20:43:01

标签: iphone objective-c uitableview

我似乎无法找到关于这个问题的任何信息,所以我想我会问社区。

基本上,我有一个UITableView,我想显示一个活动指示器,同时从我的服务器加载数据。

以下是我正在尝试做的一些示例代码(我正在使用ASIHttpRequest)。

    //self.listData = [[NSArray alloc] initWithObjects:@"Red", @"Green", @"Blue", @"Indigo", @"Violet", nil];   //this works
    NSString *urlStr=[[NSString alloc] initWithFormat:@"http://www.google.com"];   //some slow request
    NSURL *url=[NSURL URLWithString:urlStr];

    __block ASIHTTPRequest *request=[ASIHTTPRequest requestWithURL:url];
    [request setDelegate:self];
    [request setCompletionBlock:^{
        self.listData = [[NSArray alloc] initWithObjects:@"Red", @"Green", @"Blue", @"Indigo", @"Violet", nil];   //this doesn't work...
        [table reloadData];

    }];
    [request setFailedBlock:^{

    }];
    [request startAsynchronous];

对google.com的虚拟请求什么都不做 - 它只会造成延迟,在回复中我希望通过我自己网站的一些JSON响应来重新填充表格。

但是当我尝试用颜色填充表格时,没有任何反应!我只是得到一张空白的表...如果我取消注释上面的行,它工作正常,它只是在http响应中,对我来说不起作用。

任何建议都非常感谢。

编辑:

我做了一个[self.tableView reloadData];现在它有效......

3 个答案:

答案 0 :(得分:64)

  1. 停止使用ASIHTTPRequest。 NSURLConnection并不难使用,并且会产生更好,更高效的代码。
  2. 您的JSON响应应该输入数据结构而不是UI。我推荐Core Data。
  3. 数据结构应该为UITableView提供信息。我再次推荐Core Data。
  4. 我建议回顾一下MVC是如何工作的,你是设计的短路,这是核心问题。

    SPOILER

    这里有更详细的说明。首先,您希望数据检索是异步的。最容易和最可重用的方法是构建一个简单的NSOperation子类。

    @class CIMGFSimpleDownloadOperation;
    
    @protocol CIMGFSimpleDownloadDelegate <NSObject>
    
    - (void)operation:(CIMGFSimpleDownloadOperation*)operation didCompleteWithData:(NSData*)data;
    - (void)operation:(CIMGFSimpleDownloadOperation*)operation didFailWithError:(NSError*)error;
    
    @end
    
    @interface CIMGFSimpleDownloadOperation : NSOperation
    
    @property (nonatomic, assign) NSInteger statusCode;
    
    - (id)initWithURLRequest:(NSURLRequest*)request andDelegate:(id<CIMGFSimpleDownloadDelegate>)delegate;
    
    @end
    

    此子类是从URL下载内容的最基本方法。使用NSURLRequest和委托构造它。它将回调成功或失败。实施只是稍长一点。

    #import "CIMGFSimpleDownloadOperation.h"
    
    @interface CIMGFSimpleDownloadOperation()
    
    @property (nonatomic, retain) NSURLRequest *request;
    @property (nonatomic, retain) NSMutableData *data;
    @property (nonatomic, assign) id<CIMGFSimpleDownloadDelegate> delegate;
    
    @end
    
    @implementation CIMGFSimpleDownloadOperation
    
    - (id)initWithURLRequest:(NSURLRequest*)request andDelegate:(id<CIMGFSimpleDownloadDelegate>)delegate
    {
      if (!(self = [super init])) return nil;
    
      [self setDelegate:delegate];
      [self setRequest:request];
    
      return self;
    }
    
    - (void)dealloc
    {
      [self setDelegate:nil];
      [self setRequest:nil];
      [self setData:nil];
    
      [super dealloc];
    }
    
    - (void)main
    {
      [NSURLConnection connectionWithRequest:[self request] delegate:self];
      CFRunLoopRun();
    }
    
    - (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSHTTPURLResponse*)resp
    {
      [self setStatusCode:[resp statusCode]];
      [self setData:[NSMutableData data]];
    }
    
    - (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)newData
    {
      [[self data] appendData:newData];
    }
    
    - (void)connectionDidFinishLoading:(NSURLConnection*)connection
    {
      [[self delegate] operation:self didCompleteWithData:[self data]];
      CFRunLoopStop(CFRunLoopGetCurrent());
    }
    
    - (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
    {
      [[self delegate] operation:self didFailWithError:error];
      CFRunLoopStop(CFRunLoopGetCurrent());
    }
    
    @synthesize delegate;
    @synthesize request;
    @synthesize data;
    @synthesize statusCode;
    
    @end
    

    现在这个类非常可重用。您可以根据需要添加NSURLConnection的其他委托方法。 NSURLConnection可以处理重定向,身份验证等。我强烈建议您查看其文档。

    从此处,您可以从CIMGFSimpleDownloadOperation或应用程序的其他部分中分离UITableViewController。在本演示中,我们将在UITableViewController中进行演示。根据您的应用需求,您可以在任何有意义的地方启动数据下载。对于这个例子,我们将在视图出现时将其踢开。

    - (void)viewWillAppear:(BOOL)animated
    {
      [super viewWillAppear:animated];
    
      NSURLRequest *request = ...;
      CIMGFSimpleDownloadOperation *op = [[CIMGFSimpleDownloadOperation alloc] initWithURLRequest:request andDelegate:self];
      [[NSOperationQueue mainQueue] addOperation:op];
      [self setDownloadOperation:op]; //Hold onto a reference in case we want to cancel it
      [op release], op = nil;
    }
    

    现在,当视图出现时,将进行异步调用并下载URL的内容。在此代码中将通过或失败。失败第一:

    - (void)operation:(CIMGFSimpleDownloadOperation*)operation didFailWithError:(NSError*)error;
    {
      [self setDownloadOperation:nil];
      NSLog(@"Failure to download: %@\n%@", [error localizedDescription], [error userInfo]);
    }
    

    成功后我们需要解析回来的数据。

    - (void)operation:(CIMGFSimpleDownloadOperation*)operation didCompleteWithData:(NSData*)data;
    {
      [self setDownloadOperation:nil];
      NSLog(@"Download complete");
    
      //1. Massage the data into whatever we want, Core Data, an array, whatever
      //2. Update the UITableViewDataSource with the new data
    
      //Note: We MIGHT be on a background thread here.
      if ([NSThread isMainThread]) {
        [[self tableView] reloadData];
      } else {
        dispatch_sync(dispatch_get_main_queue(), ^{
          [[self tableView] reloadData];
        });
      }
    }
    

    完成了。您可以编写更多代码行,但它会替换使用ASI导入的13K +代码行,从而生成更小,更精简,更快的应用程序。更重要的是,它是一个你理解每一行代码的应用程序。

答案 1 :(得分:12)

这是问题

request setCompletionBlock:^{
        self.listData = [[NSArray alloc] initWithObjects:@"Red", @"Green", @"Blue", @"Indigo", @"Violet", nil];   //this doesn't work...
        [table performSelectorOnMainThread:@selector(reloadTable) withObject:nil waitUntilDone:NO];    
    }];

需要在主线程上完成重装表。

答案 2 :(得分:0)

我尝试过NWCoder的解决方案,但它没有用,因为它调用了错误的方法。这是我用的。
[self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];