异步加载JSON并显示活动指示器视图

问题描述

| 我正在App Delegate类中异步下载JSON Feed。现在数据需要花费一些时间来加载,因此我的表视图首先显示为空,然后在几秒钟后填充。因此,我想: 1-找出造成此延迟的原因。因此,请将所有活动保留在application:didFinishLaunchingWithOptions方法中,并且仅在加载所有内容之后才加载VC。 要么 2-显示活动指示器,直到表格填充数据为止。 现在在第一种情况下,我可以肯定在错误的时间推送了视图控制器。我尝试使用它,但这似乎是我的应用程序构建和运行的唯一方法。 在第二种情况下,我想知道首先调用哪个“连接”方法,最后调用哪个。因此,我将能够在第一种方法中启动活动指示器视图,并在最后一种方法结束时释放。 下面是我的代码。任何建议/帮助,不胜感激。感谢您的阅读。
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
        [responseData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
     [responseData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {

     UIAlertView *alert = [[UIAlertView alloc] 
                      initWithTitle:@\"Error\" 
                      message:@\"Please check your network connection and relaunch the application\" 
                      delegate:self 
                      cancelButtonTitle:@\"dismiss\" 
                       otherButtonTitles:nil,nil];
    [alert show];
    [alert release];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

    Nsstring *responseString = [[Nsstring alloc] initWithData:responseData encoding:NSUTF8StringEncoding];

    if ([responseString isEqualToString:@\"Unable to find specified resource.\"]) {
        NSLog(@\"Unable to find specified resource.n\");
    } 

        else {

        ListingsViewController *listingsViewController = [[ListingsViewController alloc] initWithNibName:@\"ListingsViewController\" bundle:nil];
        listingsViewController.jsonData = responseString;
        [self.navigationController pushViewController:listingsViewController animated:NO];
        [self.navigationController setViewControllers:[NSArray arrayWithObject:listingsViewController] animated:NO];
        [listingsViewController release];
     }

    [connection release];
    [responseData release];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Start the HTTP request
    responseData = [[NSMutableData data] retain];
    NSURLRequest *request = [NSURLRequest requestWithURL:
                         [NSURL URLWithString:@\"http://www.shoofeetv.com/iphonexml/view/all_channels.json\"]];
    [[NSURLConnection alloc] initWithRequest:request delegate:self];

    // display the navigation controller
    self.window.rootViewController = self.navigationController;
    [self.window makeKeyAndVisible];

    return YES;
}
    

解决方法

        使用选项2。它是一个很好的UI设计,可以尽快显示内容,即使在加载数据之前UI可能没有用,它至少会使您的应用程序用户感觉正在发生某些事情。 在didFinishLaunchingWithOptions中弹出UI,显示活动指示器,并在connectionDidFinishLoading中隐藏并销毁活动指示器。 我还建议将所有异步http请求逻辑包装到另一个类中,并让它接受委托,然后例如可以调用:
{
   // Show activity indicator
   [httpClient get:@\"www.example.com/json\" withDelegate:self];
}

-(void)httpClientSuccessful:(NSData*)response
{
   // hide activity indicator
}
    ,        选择2当然是正确的方法。在完成网络操作之前​​,切勿阻塞UI。如果您的收讯不佳,则不要在数秒内不响应应用程序。用户将杀死它。
NSURLConnection
委托方法以
didReceiveResponse
didReceiveData
(可能多次),
connectionDidFinishLoading
的顺序调用。
didFailWithError
可以随时被调用。 我已经成功使用了一种模式,可以立即创建并显示表格视图。只要还没有加载数据,我就会显示一个带有活动指示符的表格单元格,并显示一个文字“正在加载数据...”。 更新: 这是一些代码(仅是基本部分)。主要思想是管理当前状态(未加载,正在加载,失败或就绪):
@interface MyViewController : UITableViewController<NSURLConnectionDelegate>
{
    NSInteger state;
    NSURLConnection* connection;
    MyData* data;
}

...
@end

@implementation MyViewController

typedef enum LoadingState {
  eNotLoaded,eLoading,eFailed,eReady
} LoadingState;

- (void) viewWillAppear: (BOOL) animated
{
  [super viewWillAppear: animated];
  // Start loading the data when the table view appears for the first time
  if (state == eNotLoaded) {
    NSURLRequest request = // create the request
    connection = [NSURLConnection initWithRequest:request delegate:self];
    [request release];
    state = eLoading;
  }
}

- (void) connection: (NSURLConnection*) connection didReceiveData: (NSData*) data
{
  // record and process the received data
}

- (void) connectionDidFinishLoading: (NSURLConnection*) connection
{
  // parse the received data and store it into \'data\'
  [connection release];
  connection = nil;

  // state changed; redisplay the table view
  state = eReady;
  [[self tableView] reloadData];
}

- (NSInteger) tableView: (UITableView*) tableView numberOfRowsInSection: (NSInteger) section
{
  if (state == eReady) {
    return [data numberOfRows];
  } else {
    return 1;
  }
}

- (UITableViewCell*) tableView: (UITableView*) tableView cellForRowAtIndexPath: (NSIndexPath*) indexPath
{
  static NSString* DefaultCellIdentifier = @\"Default\";

  UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier: DefaultCellIdentifier];
  if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: DefaultCellIdentifier] autorelease];
  }
  if (state == eReady) {
    cell.textLabel.text = [data labelAtRow: indexPath.row]; 
  if (state == eLoading) {
    cell.textLabel.text = @\"Loading...\";
  } else {
    cell.textLabel.text = @\"Failed\";
  }
  return cell;
}
    ,        我已经使用了“ 2”类方法sendAsynchronousRequest:queue:completionHandler:确实非常有帮助且直接。 它让我发送一个块作为回调,以分析指定请求的错误或成功:
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url]
                                   queue:[NSOperationQueue currentQueue]
                       completionHandler:^(NSURLResponse *response,NSData *data,NSError *error) {
                           if (!error) {
                                [self parseData:data];
                           } else {
                               NSLog(@\"ERROR %@\",error);
                           }

                       }
];
这样,我可以在调用该方法之前激活活动指示器,并在检索到数据后将其删除,同时可以处理错误。 我希望这有帮助 ;)