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

Iphone 异步加载JSON并显示活动指示器视图,iphone,objective-c,cocoa-touch,json,ios4,Iphone,Objective C,Cocoa Touch,Json,Ios4,我正在我的应用程序委托类中异步下载JSON提要。现在加载数据需要一段时间,因此我的表视图首先显示为空,然后在几秒钟后填充。因此,我想: 1-找出造成延迟的原因。因此,保留应用程序中的所有活动:didFinishLaunchingWithOptions方法,仅在加载所有内容后加载VC 或 2-显示活动指示器,直到表格填充数据 现在在第一个场景中,我非常确定我在错误的时间按下了视图控制器。我试着玩它,但这似乎是我的应用程序构建和运行的唯一方式 在第二个场景中,我想知道首先调用哪个“连接”方法,最后调

我正在我的应用程序委托类中异步下载JSON提要。现在加载数据需要一段时间,因此我的表视图首先显示为空,然后在几秒钟后填充。因此,我想:

1-找出造成延迟的原因。因此,保留应用程序中的所有活动:didFinishLaunchingWithOptions方法,仅在加载所有内容后加载VC

2-显示活动指示器,直到表格填充数据

现在在第一个场景中,我非常确定我在错误的时间按下了视图控制器。我试着玩它,但这似乎是我的应用程序构建和运行的唯一方式

在第二个场景中,我想知道首先调用哪个“连接”方法,最后调用哪个。因此,我将能够在第一个方法启动ActivityIndicator视图,并在最后一个方法结束时发布

下面是我的代码。非常感谢您的任何建议/帮助。谢谢你的阅读

- (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,显示活动指示器,在ConnectiondFinishLoading中隐藏并销毁活动指示器

我还建议将所有异步http请求逻辑包装到另一个类中,并让它接受委托,例如,您可以调用:

{
   // Show activity indicator
   [httpClient get:@"www.example.com/json" withDelegate:self];
}

-(void)httpClientSuccessful:(NSData*)response
{
   // hide activity indicator
}

选择2当然是正确的选择。在网络操作完成之前,绝不应阻止UI。如果你的接收效果不好,你不希望在几秒钟内出现无响应的应用程序。用户将杀死它

NSURLConnection
委托方法的调用顺序为
didReceiveResponse
didReceiveData
(可能多次),
ConnectiondFinishLoading
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;
}
@接口MyViewController:UITableViewController
{
恩辛杰州;
NSURLConnection*连接;
MyData*数据;
}
...
@结束
@MyViewController的实现
typedef枚举加载状态{
满载,
朗诵,
埃菲尔德,
埃雷迪
}加载状态;
-(无效)视图将显示:(BOOL)动画
{
[超级视图将显示:动画];
//当表格视图第一次出现时开始加载数据
如果(状态==eNotLoaded){
NSURLRequest=//创建请求
connection=[NSURLConnection initWithRequest:request委托:self];
[请求释放];
状态=加载;
}
}
-(void)连接:(NSURLConnection*)连接didReceiveData:(NSData*)数据
{
//记录并处理接收到的数据
}
-(无效)连接IDFinishLoading:(NSURLConnection*)连接
{
//解析接收到的数据并将其存储到“数据”中
[连接释放];
连接=零;
//状态已更改;重新显示表视图
状态=eReady;
[[self tableView]重新加载数据];
}
-(NSInteger)表视图:(UITableView*)表视图行数节:(NSInteger)节
{
如果(状态==eReady){
返回[数据行数];
}否则{
返回1;
}
}
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
静态NSString*DefaultCellIdentifier=@“默认”;
UITableViewCell*单元格=[tableView dequeueReusableCellWithIdentifier:DefaultCellIdentifier];
如果(单元格==nil){
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault重用标识符:DefaultCellIdentifier]自动释放];
}
如果(状态==eReady){
cell.textLabel.text=[data-labelarrow:indexPath.row];
if(state==eLoading){
cell.textlab.text=@“正在加载…”;
}否则{
cell.textlab.text=@“失败”;
}
返回单元;
}

我使用了
NSURLConnection
类方法,这种方法非常有帮助而且简单明了

它允许我发送一个块作为回调,用于分析指定请求的错误或成功:

[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url]
                                   queue:[NSOperationQueue currentQueue]
                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
                           if (!error) {
                                [self parseData:data];
                           } else {
                               NSLog(@"ERROR %@", error);
                           }

                       }
];
通过这种方式,我可以在调用方法之前激活活动指示器,并在检索到数据后将其删除,同时可以处理错误


我希望这有帮助;)

将所有aysnc http请求包装到另一个方法或另一个类中?你知道我在哪里可以找到关于objective-c http异步方法的好教程吗?我到处都找过,但似乎还不太了解。谢谢你..在另一个类中-例如,称之为HttpClient。老实说,最好的地方是aPPE文档-通读您可以实现的所有方法以及每个方法的作用。如果您真的不理解它(或者根本不介意),请看一看-特别是异步内容。它是围绕NSURLConnection的一个很好的包装类,应该会让您的生活更轻松。您说:“我已经成功地使用了一种模式,可以立即创建并显示表视图。只要数据尚未加载,我就会显示一个带有活动指示器的单个表单元格,并显示一条文字,上面写着“正在加载数据…”。“如何???任何细节??我添加了一些代码。这只是解释模式的基本部分。真正的解决方案必须处理连接故障,在加载数据之前离开视图,正确清理,重新加载数据等。