문제

I have two UITableViewControllers A and B, and this is what I'm trying to do when I click on a table view cell in A:

  1. Prepare to segue from A to B by setting some of B's variables from A.
  2. Perform segue from A to B.
  3. B appears.
  4. Display a "Loading" activity indicator with [MBProgressHUD][1].
  5. In a background task, retrieve data from a URL.
  6. If an error occurs in the URL request (either no data received or non-200 status code), (a) hide activity indicator, then (b) display UIAlertView with an error message
  7. Else, (a) Reload B's tableView with the retrieved data, then (b) Hide activity indicator

However, this is what's happening, and I don't know how to fix it:

  1. After clicking a cell in A, B slides in from the right with an empty plain UITableView. The MBProgressHUD DOES NOT SHOW.
  2. After a while, the tableView reloads with the retrieved data, with the MBProgressHUD appearing very briefly.
  3. The MBProgressHUD immediately disappears.

There doesn't seem to be an error with the way the background task is performed. My problem is, how do I display the MBProgressHUD activity indicator as soon as my B view controller appears? (And actually, how come it's not showing?) Code is below.

A's prepareForSegue

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    B *b = (B *)[segue destinationViewController];

    // Set some of B's variables here...
}

Relevant methods in B

- (void)viewDidAppear:(BOOL)animated {
    [self startOver];
}

- (void)startOver {
    [self displayLoadingAndDisableTableViewInteractions];
    [self retrieveListings];
    [self.tableView reloadData];
    [self hideLoadingAndEnableTableViewInteractions];
}

- (void)displayLoadingAndDisableTableViewInteractions {
    MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    hud.labelText = @"Loading";
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

    self.tableView.userInteractionEnabled = NO;
}

- (void)hideLoadingAndEnableTableViewInteractions {
    [MBProgressHUD hideHUDForView:self.view animated:YES];
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

    self.tableView.userInteractionEnabled = YES;
}

- (void)retrieveListings {
    __block NSArray *newSearchResults;

    // Perform synchronous URL request in another thread.
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        newSearchResults = [self fetchNewSearchResults];
    });

    // If nil was returned, there must have been some error--display a UIAlertView.
    if (newSearchResults == nil) {
        [[[UIAlertView alloc] initWithTitle:@"Oops!" message:@"An unknown error occurred. Try again later?" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
    } else {
        // Add the retrieved data to this UITableView's model. Then,
        [self.tableView reloadData];
    }
}

- (NSArray *)fetchNewSearchResults {
    // Assemble NSMutableArray called newSearchResults from NSURLConnection data.
    // Return nil if an error or a non-200 response code occurred.
    return newSearchResults;
}
도움이 되었습니까?

해결책 2

When B appears, it displays a plain and empty UITableView, but does not display the MBProgressHUD even if the task does begin in the background (and yet, the MBProgressHUD is called to show before that). Hence, my solution is to show the MBProgressHUD in viewDidLoad, which precedes viewWillAppear.

- (void)viewDidLoad {
    // ...
    [self displayLoadingAndDisableUI];
}

I set up two additional boolean properties to B--one in .h, called shouldStartOverUponAppearing, and one in a class extension in .m, called isLoadingAndDisabledUI. In startOver, I added the following lines:

- (void)startOver {
    if (!self.isLoadingAndDisabledUI) {
        [self displayLoadingAndDisabledUI];
    }
}

The check is done so that startOver doesn't display another MBProgressHUD when it has already been displayed from viewDidLoad. That is because I have a third view controller, called C, that may call on B's startOver, but doesn't need to call viewDidLoad just to display the MBProgressHUD.

Also, this is how I defined viewDidAppear:

- (void)viewDidAppear:(BOOL)animated {
    if (self.shouldStartOverUponAppearing) {
        [self startOver];
        self.shouldStartOverUponAppearing = NO;
    }
}

This way, startOver will only be invoked IF B appeared from A. If B appears by pressing "Back" in C, it will do nothing and only display the old data that was there.

I think that this solution is FAR from elegant, but it works. I guess I'll just ask for a better approach in a separate SO question.

다른 팁

I think you have to call [self hideLoadingAndEnableTableViewInteractions]; after newSearchResults = [self fetchNewSearchResults]; You are retrieving data in another thread which means -startOver will continue executing after calling [self retrieveListings]; and will hide the HUD right away. Also because you are updating the display you have to make sure you are doing that on the main thread. See example

dispatch_async(dispatch_get_main_queue(), ^{
            //update UI here
        });

I have used a common method for MBProgressHUD.

  1. #import "MBProgressHUD.h" in AppDelegate.h also following methods.

    - (MBProgressHUD *)showGlobalProgressHUDWithTitle:(NSString *)title;
    - (void)dismissGlobalHUD;
    
  2. In AppDelegate.m add following methods.

    - (MBProgressHUD *)showGlobalProgressHUDWithTitle:(NSString *)title {
       [MBProgressHUD hideAllHUDsForView:self.window animated:YES];
       MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.window animated:YES];
       hud.labelText = title;
       return hud;
    }
    
    - (void)dismissGlobalHUD {
      [MBProgressHUD hideHUDForView:self.window animated:YES];
    }
    
  3. How to use?

    AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication]delegate];
    //Show Indicator
    [appDel showGlobalProgressHUDWithTitle:@"Loading..."];
    //Hide Indicator
    [appDel dismissGlobalHUD];
    

Hope this helps.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top