Асинхронная загрузка JSON и отображение представления индикатора активности - PullRequest
4 голосов
/ 13 июня 2011

Я загружаю фид JSON асинхронно в своем классе делегата приложения.Теперь загрузка данных занимает некоторое время, поэтому мое табличное представление сначала отображается пустым, а затем заполняется через несколько секунд.Поэтому я бы хотел:

1 - выяснить, что вызывает эту задержку.Поэтому сохраняйте все действия в приложении: метод didFinishLaunchingWithOptions и загружайте VC только после того, как все будет загружено.

ИЛИ

2 - Отображайте индикатор активности, пока таблица не заполнит данные.1007 *

Теперь в первом сценарии я почти уверен, что нажимаю контроллер представления в неправильное время.Я попытался поиграться с этим, но, похоже, это единственный способ, с помощью которого мое приложение будет собираться и запускаться.

Во втором сценарии я хотел бы знать, какой метод «подключения» вызывается первым, а какойпрошлой.Поэтому я смогу запустить представление индикатора активности в первом методе и выпустить в конце последнего метода.

Ниже приведен мой код.Любые предложения / помощь с благодарностью.Спасибо за чтение.

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

Ответы [ 3 ]

2 голосов
/ 13 июня 2011

Вариант 2, безусловно, правильный путь.Вы никогда не должны блокировать пользовательский интерфейс, пока не завершится сетевая операция.Если у вас плохой прием, вы не хотите приложение без ответа в течение нескольких секунд.Пользователь уничтожит его.

Методы делегатов 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 голосов
/ 13 июня 2011

Перейдите к варианту 2. Это хороший дизайн пользовательского интерфейса, позволяющий отображать вещи как можно быстрее, даже если пользовательский интерфейс может быть бесполезен, пока данные не загружены, это, по крайней мере, заставит пользователей вашего приложения чувствовать, что что-то происходит.

Вставьте пользовательский интерфейс в didFinishLaunchingWithOptions, отобразите индикатор активности, а в connectionDidFinishLoading скрыть и уничтожить индикатор активности.

Я также рекомендовал бы перенести всю логику асинхронного http-запроса в другой класс и разрешить его прием.делегат, то, например, вы можете позвонить:

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

-(void)httpClientSuccessful:(NSData*)response
{
   // hide activity indicator
}
0 голосов
/ 12 февраля 2013

Я использовал NSURLConnection метод класса sendAsynchronousRequest: queue: completeHandler: , который был действительно полезен и прост.

Позволяет мне отправить блок в качестве обратного вызова для анализа ошибки илиуспех для указанного запроса:

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

                       }
];

Таким образом, я могу активировать индикатор активности перед вызовом метода и удалить его после извлечения данных, и в то же время я могу обработать ошибки.

Надеюсь это поможет;)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...