Help with Sectioned UITableView from XML

Discussion in 'iPhone/iPad Programming' started by MattTrout, Oct 28, 2009.

  1. macrumors newbie

    Joined:
    Oct 28, 2009
    #1
    Hey everyone, new forum guy here with a question regarding sections in a Tableview being populated from an XML file.

    My XML looks like the following....

    <Schedule>
    <Month name="October 2009">
    <Game>
    <Date>
    <HomeTeam>
    <AwayTeam>
    <Time>
    <Result>
    </Game>
    </Month>
    <Month name="November 2009">
    <Game>
    <Date>
    <HomeTeam>
    <AwayTeam>
    <Time>
    <Result>
    </Game>
    </Month>
    </Schedule>

    I can populate all the <Game> nodes inside the Tableview fine and I am also populating an array of the <Month> names called "months" ("games" is the array for all the games). My question is how can I get a section for each <Month> and then slide the <Game> nodes for that month in the section?

    I used NSLog to count each time a month was added it had it return a count of the "months" array. But when I put return [months count]; under numberOfSectionInTableView (I just did this to make sure it was returning the right number of sections), the app crashes so I'm kind of lost.

    Anything I have been trying thrown an error or the app crashes so any help is greatly appreciated! Thanks for reading!

    --Matt
     
  2. Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #2
    Is there a run-time error message with the crash? What is it? Can you post your method's code?

    Also, you meant numberOfSectionsInTableView and not numberOfSectionInTableView, correct? ;)
     
  3. thread starter macrumors newbie

    Joined:
    Oct 28, 2009
    #3
    Haha yes my bad haha, missed the S by accident!

    Anyway the error thrown when I do return [months count]; is "numberOfSectionsInTableView must return at least one section" so it's not returning the 7 sections it tells me that array has so I'm confused.

    anyway the code pertaining to parsing and setting up the UITableview is below just so there isn't any other confusion on anything...

    Code:
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    	return [months count];
    }
    
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    	return [games count];
    }
    
    
    - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    	return @"Default";
    }
    
    - (void)parseXMLFileAtURL:(NSString *)URL
    {	
    	games = [[NSMutableArray alloc] init];
    	months = [[NSMutableArray alloc] init];
    	
    	NSString *Path = [[NSBundle mainBundle]bundlePath];
    	NSString *DataPath = [Path stringByAppendingPathComponent:@"Schedule.xml"];
    	NSData *Data = [[NSData alloc]initWithContentsOfFile:DataPath];
    	
        
        rssParser = [[NSXMLParser alloc] initWithData:Data];
    	
        [rssParser setDelegate:self];
    	
        [rssParser setShouldProcessNamespaces:NO];
        [rssParser setShouldReportNamespacePrefixes:NO];
        [rssParser setShouldResolveExternalEntities:NO];
    	
        [rssParser parse];
    	
    }
    
    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{			
        
    	currentElement = [elementName copy];
    	if ([elementName isEqualToString:@"Month"]) {
    		
    		item = [[NSMutableDictionary alloc] init];
    		monthName = [[NSMutableString alloc] initWithString:[attributeDict valueForKey:@"name"]];
    		NSLog(@"Adding A Month with name: %@", monthName);
    	}
    	
    	else if ([elementName isEqualToString:@"Game"]) {
    		
    		item = [[NSMutableDictionary alloc] init];
    		gameDate = [[NSMutableString alloc] init];
    		gameTime = [[NSMutableString alloc] init];
    		theHomeTeam = [[NSMutableString alloc] init];
    		theVisitorTeam = [[NSMutableString alloc] init];
    		theGameResults = [[NSMutableString alloc] init];
    	}
    	
    }
    
    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{     
    	
    	if ([elementName isEqualToString:@"Month"]) {
    		
    		[item setObject:monthName forKey:@"name"];
    		
    		[months addObject:[item copy]];
    		NSLog(@"Adding A New Month %@", monthName);
    		NSLog(@"months array has %d items", [months count]);
    	}
    	
    	else if ([elementName isEqualToString:@"Game"]) {
    		
    		[item setObject:gameDate forKey:@"Date"];
    		[item setObject:theVisitorTeam forKey:@"Visitor"];
    		[item setObject:theHomeTeam forKey:@"Home"];
    		[item setObject:gameTime forKey:@"Time"];
    		[item setObject:theGameResults forKey:@"Results"];
    		
    		[games addObject:[item copy]];
    		NSLog(@"Adding Game on date: %@", gameDate);
    	}
    	
    }
    
    - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
    	
    	if ([currentElement isEqualToString:@"Month"]) 
    	{
    		[monthName appendString:string];
    	}
    	else if ([currentElement isEqualToString:@"Date"]) 
    	{
    		[gameDate appendString:string];
    	} 
    	else if ([currentElement isEqualToString:@"Visitor"]) 
    	{
    		[theVisitorTeam appendString:string];
    	} 
    	else if ([currentElement isEqualToString:@"Home"]) 
    	{
    		[theHomeTeam appendString:string];
    	} 
    	else if ([currentElement isEqualToString:@"Time"])
    	{
    		[gameTime appendString:string];
    	} 
    	else if ([currentElement isEqualToString:@"Results"]) 
    	{
    		[theGameResults appendString:string];
    	}
    	
    	
    }
    
    Thanks for the reply!
     
  4. Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #4
    Two things:

    1) Are you attempting to display your table before you parse the xml? If so, months is empty (nil) and won't be able to return a count. You should add a fall-back return to numberOfSectionsInTableView:, like so:
    Code:
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
        if (months != nil)
    	return [months count];
        else
            return 1;
    }
    
    2) I think you need to redo your table data-source model, since the way it is currently, you are going to get the list of all games in each section / month. You probably want to look at doing an array of months and then each of those array elements will be another array of the games for that month. Of course, you'll need to make appropriate changes to your xml-parsing and data-source methods to handle this new approach.
     
  5. thread starter macrumors newbie

    Joined:
    Oct 28, 2009
    #5
    Sounds good...yeah I added the if else to the numberOfSections method and it worked fine..I never thought about that.

    Is there anyway I could use string compare functions to compare the value of the date from the XML and say if(date value contains string="Oct") for instance that I could tell it to addObject to OctoberGames array and then make one for each month and just load it that way?
     
  6. Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #6
    You could. Seems a tad hard-coded though. Build a class to model your month. Very simply, it would contain, at least, a string (that can be used as the section-header text) of the month's name as well as an array of games. Build a class to model your game. It would contain the date, home-team, away-team, etc. Then the data-source for your table is an array containing Month objects. Make sense?
     
  7. thread starter macrumors newbie

    Joined:
    Oct 28, 2009
    #7
    Makes sense logically only I'm not so sure I know how to start it...(semi newbie).
     
  8. Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #8
    Then perhaps it is time to step back from the real coding and go learn the basics of Objective-C, OOP, Model-View-Controller, etc. What is your education / experience with these things? Have you read any books, taken any classes, things of this nature?
     
  9. thread starter macrumors newbie

    Joined:
    Oct 28, 2009
    #9
    Nah I'm more a learn by doing kind of person. I haven't read books but read a lot about obj c/iphone/xcode over the past couple months that I've been tinkering across the internet and stuff. I'm more of a java/flash/actionscript guy, obj c is different so i'm just trying to get used to it.
     
  10. Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #10
    Well, I would start with learning how to create classes that you can use to model Game and Month. You already have Game kinda modeled using an NSDictionary but it wouldn't hurt to give it its own class as well.
     
  11. macrumors newbie

    Joined:
    Nov 26, 2009
    #11
    UITableView cellforrowatindexpath not called

    Hi All,

    I am pretty new to Iphone development . so please bear me if I ask some very simple questions.

    In my application I have multiple views(i.e. .xib files). On clicking the button on the main view(CouponWebsiteViewController.Xib) the application should load the second view(BrowseList.Xib) which contains a UITable and I have to populate that UITable with some data. I have written following code to populate the data on BrowseList.m file:

    Code:
    (void) viewDidLoad
    {
    arrayData = [[NSArray alloc] init]; arrayData = [arrayData arrayByAddingObject:@"dfsgdf"]; arrayData = [arrayData arrayByAddingObject:@"aaaaaa"]; self.lblNewScreen.text = [arrayData objectAtIndex:0]; [super viewDidLoad];
    }
    
    
    
    - (NSInteger)numberOfSectionsInTableViewUITableVie w *)tableView
    {
    return 1;
    }
    
    - (NSInteger)tableViewUITableView *)tableView numberOfRowsInSectionNSInteger)section
    {
    return [arrayData count];
    }
    
    
    - (UITableViewCell *)tableView;(UITableView *)tableView cellForRowAtIndexPath;(NSIndexPath *)indexPath
    {
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
    {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
    
    NSString *cellValue = [arrayData objectAtIndex:indexPath.row];
    cell.textLabel.text = cellValue;
    [tableView setEditing:YES animated:YES];
    return cell;
    
    }
    But It is not populating the data in table , when I debug this code I found that it is not executing cellForRowAtIndexPath method but it is debugging numberOfRowsInSection and numberOfSectionsInTableView methods.

    But the interesting thing is when I write the same code on the CouponWebsiteViewController.m (i.e. on main view) then it is populating the data in table.

    The point is this code is working fine on the main view but it does not work for the other views.

    Can any one tell me what am I missing or any other way to populate the UITable on views other than the main view.

    Thanks in Advance. Gaurav
     
  12. Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #12
    EDIT:
    Code:
    - (UITableViewCell *)tableView[COLOR="Red"][B];[/B][/COLOR](UITableView *)tableView cellForRowAtIndexPath[COLOR="Red"][B];[/B][/COLOR](NSIndexPath *)indexPath
    sapplesystems, be aware that ';' is not the same as ':' and ends your line.
     
  13. macrumors newbie

    Joined:
    Nov 26, 2009
    #13
    Yes I know that , it was just by mistake . But I am using ':' only in my code.
    Can you please help me in solving my issue.
    Am sorry for the mistake.:mad:
     
  14. Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #14
    Where and how is arrayData declared? I would suspect that when tableView:numberOfRowsInSection: is being called, it is returning 0.

    Also, you sure you need to be calling setEditing:animated: for the tableView when setting up each cell?
     
  15. macrumors 6502

    Joined:
    Jun 19, 2008
    Location:
    St Louis
    #15
    Then I recommend you snag a copy of Beginning iPhone 3 Development, it's got a lot of great examples and you'll learn "by doing".
     
  16. iJar82, Mar 15, 2011
    Last edited: Mar 15, 2011

    macrumors newbie

    Joined:
    Mar 15, 2011
    #16
    Same Issue different approach

    Hi dejo & MattTrout,

    Happy to see you succeeded creating an UITableview with sections from an XML file. I have the same problem but used a slightly different approach. I followed dejo approach and created a class for my 'Stage' (yours would probably be called 'Game')
    As my script is now it works perfectly but I'd like to add sections to it and I am stuck on this for a while now, please have a look at it?
    I think I also have to create a class for my Month like you explained before dejo but how do I implement this in the structure how I have it now and make my stages a piece of every month?

    This is my xml file Stages.xml:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <Stages>
      <Month id="1" name="January">
        <Stage id="1">
          <title>Circumference</title>
          <author>Nicholas Nicastro</author>
          <summary>Eratosthenes and the Ancient Quest to Measure the Globe.</summary>
        </Stage>
        <Stage id="2">
          <title>Copernicus Secret</title>
          <author>Jack Repcheck</author>
          <summary>How the scientific revolution began</summary>
        </Stage>
      </Month>
      <Month id="2" name="February">
        <Stage id="3">
          <title>Angels and Demons</title>
          <author>Dan Brown</author>
          <summary>Robert Langdon is summoned to a Swiss research facility to analyze a cryptic symbol seared into the chest of a murdered physicist.</summary>
        </Stage>
        <Stage id="4">
          <title>Keep the Aspidistra Flying</title>
          <author>George Orwell</author>
          <summary>A poignant and ultimately hopeful look at class and society, Keep the Aspidistra Flying pays tribute to the stubborn virtues of ordinary people who keep the aspidistra flying.</summary>
        </Stage>
      </Month>
    </Stages>
    This is the code of my XMLParser.m file:
    Code:
    #import "XMLParser.h"
    #import "DAFAppDelegate.h"
    #import "Stage.h"
    
    @implementation XMLParser
    
    - (XMLParser *) initXMLParser
    {
    	[super init];
    	appDelegate = (DAFAppDelegate *)[[UIApplication sharedApplication] delegate];
    	return self;
    }
    
    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
    {
    	
    	if([elementName isEqualToString:@"Stages"])
        {
    		//Initialize the array.
    		appDelegate.stages = [[NSMutableArray alloc] init];
    	}
    	else if([elementName isEqualToString:@"Stage"])
        {
    		
    		//Initialize the Stage.
    		aStage = [[Stage alloc] init];
    		
    		//Extract the attribute here.
    		aStage.stageID = [[attributeDict objectForKey:@"id"] integerValue];
    		
    		NSLog(@"Reading id value :%i", aStage.stageID);
    	}
    	
    	NSLog(@"Processing Element: %@", elementName);
    }
    
    - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
    { 
    	if(!currentElementValue)
        {
    		currentElementValue = [[NSMutableString alloc] initWithString:string];
    	}
        else
        {
    		[currentElementValue appendString:string];
    	}
    	NSLog(@"Processing Value: %@", currentElementValue);
    	
    }
    
    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
    {
    	
    	if([elementName isEqualToString:@"Stages"])
    		return;
    	//There is nothing to do if we encounter the Stages element here.
    	//If we encounter the Book element howevere, we want to add the book object to the array
    	// and release the object.
    	if([elementName isEqualToString:@"Stage"])
        {
    		[appDelegate.stages addObject:aStage];
    		[aStage release];
    		aStage = nil;
    	}
    	else
        {
        [aStage setValue:currentElementValue forKey:elementName];
    	[currentElementValue release];
    	currentElementValue = nil;
        }
    }
    
    - (void) dealloc {
    	[aStage release];
    	[currentElementValue release];
    	[super dealloc];
    }
    
    @end
    How do I insert the layer of sections to my code?
     
  17. macrumors newbie

    Joined:
    Mar 15, 2011
    #17
    Created the Month class but now tie them together

    Dear Dejo,

    I am new to this forum as well as I am to developing for the iPhone.
    Could you please help me out on this one? Or steer me into the right direction?
    I have created the Month class now and also had the Stage class(game class in MattTrout his example). But how do I tie them together in my XMLParser class?

    My Stage.h file:
    Code:
    #import <Foundation/Foundation.h>
    
    @interface Stage : NSObject
    {
    	
    NSInteger stageID;
    NSString *title;        //Same name as the Entity Name.
    NSString *author;	//Same name as the Entity Name.
    NSString *summary;	//Same name as the Entity Name.
    
    }
    
    @property (nonatomic, readwrite) NSInteger stageID;
    @property (nonatomic, retain) NSString *title;
    @property (nonatomic, retain) NSString *author;
    @property (nonatomic, retain) NSString *summary;
    
    @end
    My Month.h file:
    Code:
    #import <Foundation/Foundation.h>
    
    @interface Month : NSObject
    {
        NSInteger       monthID;
        NSString        *name;
        NSMutableArray  *monthStage;
    }
    
    @property (nonatomic, readwrite) NSInteger monthID;
    @property (nonatomic, retain) NSString *name;
    @property (nonatomic, retain) NSMutableArray *monthStage;
    
    @end
     

Share This Page