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

Eam*_*orr 18 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];
Run Code Online (Sandbox Code Playgroud)

对google.com的虚拟请求什么都不做 - 它只是创建了一个延迟,在响应中我希望用我自己网站上的一些JSON响应来重新填充表格.

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

任何建议都非常感谢.

编辑:

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

Mar*_*rra 64

  1. 停止使用ASIHTTPRequest. NSURLConnection不难使用,会产生更好,更高性能的代码.
  2. 您的JSON响应应该输入数据结构而不是UI.我推荐Core Data.
  3. 数据结构应该喂你的UITableView.我再次推荐Core Data.

我建议回顾一下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
Run Code Online (Sandbox Code Playgroud)

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

#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
Run Code Online (Sandbox Code Playgroud)

现在这个课非常可重复使用.您可以根据需要添加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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

成功之后,我们需要解析返回的数据.

- (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];
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 告诉他停止使用ASI作为你的第一个建议实际上没有帮助.ASI是一个很棒的图书馆.你应该100%使用它吗?也许不是,但可能是99%.在任何情况下,他的问题都与从后台线程更新UI有关,这显然是问题的原因.核心数据?!?!?不是所有问题的一部分.虽然在视图控制器中直接轻松地不消耗原始API响应是一个好主意,盲目地告诉他进行剧烈的体系结构更改绝对没有帮助. (17认同)
  • ASI是13K代码行来代替开发人员的工作****可能是**100行代码或200行代码.它接受一个*巨大*黑盒子,这样开发人员可以节省100行代码.ASI是懒惰和臃肿.人们不喜欢它是有原因的.这是一个不必要的拐杖; 一个糟糕的重新实现,因为作者很可能不了解`NSURLConnection`开始并担心他不理解. (8认同)
  • `NSURLConnection`是未来.说真的,它很简单,涵盖了我遇到过的每一个用例.除非您确切知道它正在做什么,否则永远不要向您的应用添加第三方框架. (7认同)
  • 裸露的`NSURLConnection`的一个不错的替代方案是优秀的`PRPConnection`,可以作为保罗·沃伦和马特·达伦更优秀的[iOS食谱](http://pragprog.com/book/cdirec/ios-recipes)的示例代码. .`PRPConnection`为`NSURLConnection`提供了一个非常精简的基于块的包装,为您提供如下方法:`+(id)connectionWithURL:(NSURL*)requestURL progressBlock:(PRPConnectionProgressBlock)progress completionBlock:(PRPConnectionCompletionBlock)completion` (2认同)

NWC*_*der 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];    
    }];
Run Code Online (Sandbox Code Playgroud)

重载表需要在主线程上完成.