Question

I have a UITableView that loads content from a xml file inside a webserver.

  • The TableView is inside a tab Bar Controller;
  • I Call [table reloadData] inside the TBXMLSuccessBlock;
  • The problem is that the content of the TableView dont update.
  • I am using TBXML library;

After the Button have been clicked, How to says to tableVIew that XML file have been downloaded and show it?

- (IBAction)leftButton:(UIButton *)sender {
    if (a<=2 && a != 0) {
        a = a - 1;

        NSString *leftURL = self.getDate;
        [self loadURL:leftURL];
    }

}

- (void)loadURL:(NSString *)newURL{

    if (newURL == NULL) {
        newURL = self.getDate;
    }

    // Create a success block to be called when the asyn request completes
    TBXMLSuccessBlock successBlock = ^(TBXML *tbxmlDocument) {
        NSLog(@"PROCESSING ASYNC CALLBACK");

        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
        // If TBXML found a root node, process element and iterate all children
        if (tbxmlDocument.rootXMLElement)
        {
            // Obtain root element
            TBXMLElement * root = tbxml.rootXMLElement;
        if (root)
        {
            _storeArray = [NSMutableArray new];
            TBXMLElement * elem_PLANT = [TBXML childElementNamed:@"news" parentElement:root];
            while (elem_PLANT !=nil)
            {
                TBXMLElement * elem_title = [TBXML childElementNamed:@"title" parentElement:elem_PLANT];
                NSString *titleName = [TBXML textForElement:elem_title];

                TBXMLElement * elem_artist = [TBXML childElementNamed:@"text" parentElement:elem_PLANT];
                NSString *artistName = [TBXML textForElement:elem_artist];

                TBXMLElement * elem_thumb = [TBXML childElementNamed:@"thumb_url" parentElement:elem_PLANT];
                NSString *thumbName = [TBXML textForElement:elem_thumb];

                NSDictionary *dictionary = [[NSDictionary alloc]initWithObjects:@[titleName, artistName, thumbName] forKeys:@[@"title", @"text", @"thumb"]];
                elem_PLANT = [TBXML nextSiblingNamed:@"news" searchFromElement:elem_PLANT];
                [_storeArray addObject:dictionary];

            }

        }

          [_tableView reloadData];
        }
    };

    // Create a failure block that gets called if something goes wrong
    TBXMLFailureBlock failureBlock = ^(TBXML *tbxmlDocument, NSError * error) {
        NSLog(@"Error! %@ %@", [error localizedDescription], [error userInfo]);
    };

    // Initialize TBXML with the URL of an XML doc. TBXML asynchronously loads and parses the file.
    tbxml = [[TBXML alloc] initWithURL:[NSURL URLWithString:newURL]
                               success:successBlock
                               failure:failureBlock];
}

EDIT: ReloadData inside buttonClick

- (IBAction)leftButton:(UIButton *)sender {
    if (a<=2 && a != 0) {
        a = a - 1;

        NSString *leftURL = self.getDate;
        [self loadURL:leftURL];
        [_tableView reloadData];
    }

    }

- (IBAction)rightButton:(UIButton *)sender {
    if (a<2) {
        a = a + 1;
        NSString *rightURL = self.getDate;
        [self loadURL:rightURL];
        [_tableView reloadData];
    }
}

When app launch: - URL number 1 is loaded; - URL number 1 is showed; When I click one time right: - URL number 2 is loaded; - Nothing happen; When I click one more time right: - URL number 3 is loaded; - URL number 2 is showed; When I click one time left: - URL 2 is loaded; - URL 3 is showed; When I click one more time left: - URL 1 is loaded; - URL 2 is showed;

Was it helpful?

Solution

You said:

When app launch: - URL number 1 is loaded; - URL number 1 is showed; When I click one time right: - URL number 2 is loaded; - Nothing happen; When I click one more time right: - URL number 3 is loaded; - URL number 2 is showed; When I click one time left: - URL 2 is loaded; - URL 3 is showed; When I click one more time left: - URL 1 is loaded; - URL 2 is showed;

From this, we know:

  1. URL/XML loading works—the data is there to be displayed, but the display isn't happening when it should.
  2. Table reloading works—on a later button press, the earlier data is displayed, but there is different data being displayed.
  3. Table reloading is happening too early—that's why it's showing the previous URL/XML's data.

Your button actions are (indirectly) making the URL-loading call using a library that takes success/failure block(s), so here's what's happening, roughly:

  1. The IBAction calls loadUrl:, which runs TBXML's initWithURL:success:failure:
  2. initWithURL:success:failure: stores the success and failure blocks and starts the actual network action on a separate thread ("in the background"), and returns, almost certainly before the network action has finished.
  3. The IBAction calls reloadData.
  4. Later, in the background, the network action finishes and the success or failure block is run.

So, moving the reloadData call into the success block makes it so that the table is reloaded after the XML data is processed.

But initially this crashed with an error that included:

This may be a result of calling to UIKit from a secondary thread.

UIKit calls—roughly, any method call that affects the user interface—have to be made on the main thread. For some asynchronous libraries that use block callbacks (notably AFNetworking), the block callbacks are guaranteed to be run on the main thread for just this reason. With TBXML, apparently, the block callbacks are run in the background, probably on the same thread that did the actual network loading action. So, we need some way to make the reloadData call to the UITableView happen on the main thread.

There are several ways to do this. My first preference is usually to use the main operation queue (which we get using the mainQueue class method on NSOperationQueue), which is a queue of operations to perform on the main thread:

[[NSOperationQueue mainQueue] addOperationWithBlock:^{
    [_tableView reloadData];
}];

The main operation queue then automatically handles executing the block as soon as it can on the main thread. Alternately, but still at the Objective-C/Cocoa/CocoaTouch level, you can use performSelectorOnMainThread:withObject:waitUntilDone: on your table view:

[_tableView performSelectorOnMainThread:@selector(reloadData)
                             withObject:nil
                          waitUntilDone:NO];

(or YES for wait until done—probably doesn't matter in this case), which has exactly the same effect. A third option, though generally not preferred in this case, is to use Grand Central Dispatch directly:

dispatch_async(dispatch_get_main_queue(), ^{
    [_tableView reloadData];
}];

(but when there are higher-level options like the NSOperationQueue route or the performSelectorOnMainThread:withObject:waitUntilDone: route, they should be used instead of this kind of lower-level C Grand Central Dispatch routine; also, changing dispatch_async to dispatch_sync has the same effect as changing waitUntilDone:NO to waitUntilDone:YES in the previous example).

OTHER TIPS

as you r saying,you have call method reloadData then it should refresh the table view..i think then problem is not there,have u tried someone without using tab bar controller..might be sometimes bcoz of tab bar controller problem may arises...so just try it once..& let us know.. or we help u in some another way

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top