UITableViewController: Data Parses but `numberOfRowsInSection:` returns NULL.
I have a UITableView populated by data parsed from xml. The parsing works but the table remains blank.
The console shows that the xml form the url is parsed and shows its components. It also shows the number of objects that the rows of tableview should have when asked in a different function but the numberOfRowsInSection:
is returning Null
. Therefore, the tableView in the Simulator remains blank.
Here is my code. It is simple code from a tutorial:
+++++++++++++++++ RootViewController.h++++++++++++++++++++++
#import < UIKit/UIKit.h >
@interface RootViewController : UITableViewController < NSXMLParserDelegate >{
IBOutlet UITableView *newsTable;
UIActivityIndicatorView *activityIndicator;
CGSize cellSize;
NSXMLParser *rssParser;
NSMutableArray *stories;
NSMutableDictionary *item;
NSString *currentElement;
NSMutableString *currentTitle, *currentDate, *currentSummary, *currentLink;
}
@property (nonatomic, retain) NSMutableArray *stories;
@property (nonatomic, retain) IBOutlet UITableView *newsTable;
-(void)parseXMLFileAtURL:(NSString *)URL;
@end
+++++++++++++++++++++++++++ RootViewController.m ++++++++++++++++++++++++++++++++++++++
#import "RootViewController.h"
@implementation RootViewController
@synthesize newsTable, stories;
-(void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[newsTable reloadData];
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if([stories count] == 0){
NSString *path = @"http://feeds.feedburner.com/TheAppleBlog";
[self parseXMLFileAtURL:path];
}
cellSize = CGSizeMake([newsTable bounds].size.width, 60);
}
// Customize the number of sections in the table view.
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(void)parseXMLFileAtURL:(NSString *)URL {
stories = [[NSMutableArray alloc] init];
NSURL *xmlURL = [NSURL URLWithString:URL];
rssParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
[rssParser setDelegate:self];
[rssParser setSho开发者_如何转开发uldProcessNamespaces:NO];
[rssParser setShouldReportNamespacePrefixes:NO];
[rssParser setShouldResolveExternalEntities:NO];
[rssParser parse];
}
-(void)parserDidStartDocument:(NSXMLParser *)parser{
NSLog(@"Found file and started parsing");
}
-(void) parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{
NSString *errorString = [NSString stringWithFormat:@"Unable to download story feed from the website (error code %i)", [parseError code]];
NSLog(@"error parsing XML: %@", errorString);
UIAlertView *errorAlert = [[UIAlertView alloc]:@"Error loading content" message:errorString delegate:self cancelButtonTitle:@"OK" otherButtonTitle:nil];
[errorAlert show];
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
NSLog(@"Found this Element %@", elementName);
currentElement = [elementName copy];
if ([elementName isEqualToString:@"item"]) {
item = [[NSMutableDictionary alloc] init];
currentTitle = [[NSMutableString alloc] init];
currentDate = [[NSMutableString alloc] init];
currentSummary = [[NSMutableString alloc] init];
currentLink = [[NSMutableString alloc] init];
}
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
NSLog(@"End this Element %@", elementName);
if ([elementName isEqualToString:@"item"]) {
[item setObject:currentTitle forKey:@"title"];
[item setObject:currentLink forKey:@"link"];
[item setObject:currentSummary forKey:@"summary"];
[item setObject:currentDate forKey:@"date"];
[stories addObject:[item copy]];
NSLog(@"adding Story : %@",currentTitle);
}
}
// Customize the number of rows in the table view.
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(@"Count is = %@", [stories count]);
return [stories count];
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
NSLog(@"Found characters: %@", string);
if([currentElement isEqualToString:@"title"]){
[currentTitle appendString:string];
NSLog(@"The Title is : %@", currentTitle);
}
else if([currentElement isEqualToString:@"link"]){
[currentLink appendString:string];
NSLog(@"The Link is : %@", currentLink);
}
else if([currentElement isEqualToString:@"description"]){
[currentSummary appendString:string];
NSLog(@"The summary is : %@", currentSummary);
}
else if([currentElement isEqualToString:@"pubDate"]){
[currentDate appendString:string];
NSLog(@"The Date is : %@", currentDate);
}
}
-(void)parserDidEndDocument:(NSXMLParser *)parser{
[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
NSLog(@"Stories array has %d items", [stories count]);
NSLog(@"Stories are : %@",stories);
}
// Customize the appearance of table view cells.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = @"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];
}
// Configure the cell.
cell.textLabel.text = (NSString *)[[stories objectAtIndex:indexPath.row] objectForKey:@"title"];
return cell;
}
-(void)dealloc {
[super dealloc];
[newsTable release];
[currentDate release];
[currentElement release];
[currentSummary release];
[currentLink release];
[stories release];
[item release];
[currentTitle release];
[rssParser release];
}
You have to tell the table view that there is new data by calling reloadData
after you have parsed the XML.
Is the newsTable
outlet properly connected to the table view in IB? And, is the table's dataSource
outlet set to your view controller?
To expand on @omz's correct answer:
The method numberOfRowsInSection
is not returning NULL but zero. (In Objective-C, nil==zero and Null is a singleton object.)
The only reason that it would return zero is if the [stories count]
returns zero and the only reason [stories count]
would return zero is if it has no elements. Since you've confirmed that the parse works and stories
has elements, then the tableview must be seeking data before the parse occurs.
This method is called first and it is the only place you reload data:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[newsTable reloadData];
// You trigger the tableview to call numberOfRowsInSection before stories is populated.
}
This method is called only after the tableview has appeared on screen and it is only after the tableview appears that you populate stories
.
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if([stories count] == 0){
NSString *path = @"http://feeds.feedburner.com/TheAppleBlog";
[self parseXMLFileAtURL:path];
}
cellSize = CGSizeMake([newsTable bounds].size.width, 60);
}
However, nothing triggers the tableview to call numberOfRowsInSection
again so the tableview remains blank. Simply moving the populating of stories to viewWillAppear:
will fix the problem.
Each time you alter the data upon which the tableview depends (regardless of reason) you must call reloadData
otherwise the tableview remains unaware that it no longer displays the current data set.
As an aside, you should use the dot notation when referring to properties to ensure they are properly retained. You should use self.stories
to refer to the stories
property. Otherwise, it might be released at random causing an equally random crash.
精彩评论