didSelectRowAtIndexPath with section

Discussion in 'iOS Programming' started by SimonBS, Feb 7, 2011.

  1. SimonBS macrumors regular

    SimonBS

    Joined:
    Dec 30, 2009
    #1
    Hi,

    I have a UITableView in which I have added sections. When using didSelectRowAtIndex with indexPath.row I am not getting the correct value. Let's say I'm in section 2, then it starts the row index (indexPath.row) at 0 again and therefore I am getting the wrong index.

    Can anyone tell me how to fix this? I realize that it is possible to get the section index by indexPath.section but I can't figure out how to use this.

    Code:
    bandDetailViewController.band = [self.programArray objectAtIndex:indexPath.row];
    I hope you can help me. Thanks in advance :)
     
  2. dalearyous macrumors newbie

    Joined:
    Oct 24, 2010
    #2
    i am having the exact same problem but instead of different sections the index starts over after you begin to type and the search/table gets narrowed down to 1 item ... always loads item in 0 position in the array :(

    not to hijack your thread but here is mine:
    Code:
    - (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
    {
    	/*
    	 Update the filtered array based on the search text and scope.
    	 */
    	
    	[self.filteredListContent removeAllObjects]; // First clear the filtered array.
    	
    	/*
    	 Search the main list for Physicians whose type matches the scope (if selected) and whose name matches searchText; add items that match to the filtered array.
    	 */
    	for (Physician *physician in listContent)
    	{
    		if ([scope isEqualToString:@"All"] || [physician.type isEqualToString:scope])
    		{
    			NSComparisonResult result = [physician.name compare:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [searchText length])];
                if (result == NSOrderedSame)
    			{
    				//here is the issue, i want something like addObject:(correct index value)
    				[self.filteredListContent addObject:physician];
                }
    		}
    	}
    }
    
     
  3. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #3
    @Simon, that's how sectioned tables work. Each section has an index and each row in a section has an index that starts from zero. Normally to present data in a sectioned table you need to have a data model that is also hierarchical.

    @D, normally filtered results are displayed in a table with no sections, even if the filtered table has sections. If you want to display the filtered results in a sectioned table then you need to build a hierarchical data structure (or else you'll go mad).
     
  4. dalearyous macrumors newbie

    Joined:
    Oct 24, 2010
    #4
    @PhoneyDeveloper, i think you might have misunderstood.

    basically the first array let say has 4 names: (0)bob, (1)jim, (2)david, & (3)carl. when you select anyone one it loads their correct detailview based on the index (0,1,2,3). however, when you go to search and type d it narrows the table down to just (0)david which is now in row 0. so when you select david it loads bob from the original array.

    someone told me to either retain the original value of indexPath.row (prior to the search), or pass it to a new index variable ... no idea how to do that lol
     
  5. SimonBS thread starter macrumors regular

    SimonBS

    Joined:
    Dec 30, 2009
    #5
    @PhoneyDeveloper I see. So the dictionaries (which consists of band names, 'name', and a day of the week, 'weekDay') should be loaded into an array with the section titles?

    So if I want to sort my bands after which day in a week they play (oh, this code is for a small festival application), I will load my array with the days (Wednesday - Saturday) and sort all my dictionaries under the days in the array after which day the band will play, right?

    If that is so, may I ask you to give a simple code example om this? I Can imagine how it will work but honestly not how it should be written.
    I'm new to softwareprogramming (Obj-C in particular) but have a basic understandment of programming after eight years of web development.
     
  6. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #6
    OK, your filteredListContent list holds the same objects as your listContent array. It just doesn't have all of them. In your didSelectRowAtIndex path you need to check which table the user is clicking in and use the correct data list. In pseudo code

    Code:
    if (showingFilteredList)
      physicianToShow = [filteredList objectAtIndex:indexPath.row];
    else // if showing the main list
     physicianToShow = [listContent objectAtIndex:indexPath.row];
    
    // push the detail view here and tell it to show the physicianToShow
     
  7. dalearyous macrumors newbie

    Joined:
    Oct 24, 2010
    #7
    @PhoneyDeveloper, i think thats what i am doing unless i missed something

    Code:
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        UIViewController *detailsViewController = [[UIViewController alloc] init];
    	/*
    	 If the requesting table view is the search display controller's table view, configure the next view controller using the filtered content, otherwise use the main list.
    	 */
    	Physician *physician = nil;
    	if (tableView == self.searchDisplayController.searchResultsTableView)
    	{
            physician = [self.filteredListContent objectAtIndex:indexPath.row];
        }
    	else
    	{
            physician = [self.listContent objectAtIndex:indexPath.row];
        }
    	detailsViewController.title = physician.name;
    	
    	NSString* physicianName = [tmpImages objectAtIndex:indexPath.row];
    	UIImage* physicianImage = [UIImage imageNamed:physicianName];
    	UIImageView* physicianImageView = [[UIImageView alloc] initWithImage:physicianImage];
    	physicianImageView.frame = self.view.bounds;
    	[detailsViewController.view addSubview:physicianImageView];
    	[physicianImageView release];
    	
    	
        [[self navigationController] pushViewController:detailsViewController animated:YES];
        [detailsViewController release];
    }
    
     
  8. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #8
    This line probably isn't right

    Code:
    NSString* physicianName = [tmpImages objectAtIndex:indexPath.row];
    
    Anyway, the code you show is a bad idea. Building an image view and adding it to another view controller's view is a terrible idea.

    What you should do is add an @property for a Physician* to your detail view controller. Set that when you create the detailViewController. Then load whatever info needs to be loaded from the Physician object in the view controller's viewDidLoad method for display.
     
  9. SimonBS thread starter macrumors regular

    SimonBS

    Joined:
    Dec 30, 2009
    #9
    PhoneyDeveloper, I imagine that I should use nested arrays for my section-issue. Would you mind giving an example on this?
     
  10. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #10
    @Simon, the data structure I usually use for a sectioned tableview is an array of arrays of dictionaries. The dictionaries represent each row in the table. The top level array holds the sections. The second level arrays each represent a section. I put the section title into dictionary of the first rows for each section (which I know isn't beautiful but it's easy and it works).

    When the view controller loads the code builds this data model from external data, often coming from a database. Note that it isn't required for all the rows of the table to exist at that time. It's possible to load section arrays at a later time when the table asks for them. However your code should know how many rows for each section exist, even if they're not loaded yet.

    This is some edited code from one of my tables that works like this. It's called from viewDidLoad. I don't show the code for the various tableView callbacks but most of them are very simple since the data model itself tells how many rows and sections there are and the dictionary for each row has all the info in it to build a row.


    Code:
    	mSectionsList = [[NSMutableArray alloc] initWithCapacity:10];// top level array
    
    	NSMutableArray*	sectionArray = [[NSMutableArray alloc] init];
    	NSDictionary*	rowDictionary;
    	NSString*		label = nil;
    	NSString*		text = nil;
    	NSString*		title = nil;
    	
    	// Add notes if they exist
    	NSArray*	noteStructures = self.noteStructures;
    	
    	for (MNoteStructure* note in noteStructures)
    	{
    		label = [self labelForNote];
    		text = [note objectForKey:kNoteStructureTextKey];
    		rowDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:label, kLabelKey, text, kTextKey, nil];
    		[sectionArray addObject:rowDictionary];
    		[rowDictionary release];
    	}
    	
    	[mSectionsList addObject:sectionArray];
    	[sectionArray release];
    
    	NSArray*		multimedia = self.multimedia;
    	sectionArray = [[NSMutableArray alloc] init];
    	title = NSLocalizedString(@"Multimedia", @"Title for Multimedia stuff");
    	NSInteger		row = 0;
    	
    	for (MMultimediaRecord* object in multimedia)
    	{
    		text = [self multimediaTextForRow:row];
    		label = [self labelForRow:row];
    		if (title)
    		{
    			rowDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:label, kLabelKey, text, kTextKey, title, kSectionTitleKey,  nil];
    			title = nil;
    		}
    		else
    			rowDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:label, kLabelKey, text, kTextKey, nil];
    
    		row++;
    		[sectionArray addObject:rowDictionary];
    		[rowDictionary release];
    	}
    
    	[mSectionsList addObject:sectionArray];
    	[sectionArray release];
    
    // More sections added here ...
    
     
  11. SimonBS thread starter macrumors regular

    SimonBS

    Joined:
    Dec 30, 2009
    #11
    Thank you very much for your help. I ended up saving my sections as dictionaries in a dictionary. And under every section I stored my rows as dictionaries. You have been a great help!
     
  12. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #12
    Curious: what are you using as keys in these dictionaries?
     
  13. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #13
    I know that some suggest an array of section dictionaries, where there is an array of row dictionaries held in the section dictionaries. The title of the section also goes into the section dictionaries. This model is slightly more flexible than the array of array of dictionaries that I usually use but also more complex.

    Don't know what Simon is actually doing though.
     
  14. SimonBS thread starter macrumors regular

    SimonBS

    Joined:
    Dec 30, 2009
    #14
    The keys in my dictionaries is the section header.

    So for example the A-section in my alphabetically sorted list would look like this.

    Code:
    A =     (
                    {
                date = "2011-02-04 20:09:40 +0000";
                description = "Long description.";
                hasDate = 1;
                name = Alphabeat;
                scene = "Store Scene";
                weekDay = Wednesday;
                youtubeVideo = "http://www.youtube.com/watch?v=dB01PTZNpBc";
            },
                    {
                date = "2011-02-04 20:09:40 +0000";
                description = "Long description.";
                hasDate = 1;
                name = "Anne Linnet";
                scene = "Store Scene";
                weekDay = Wednesday;
                youtubeVideo = "http://www.youtube.com/watch?v=jWMSqS7fL9k";
            },
                    {
                date = "2011-02-04 20:09:40 +0000";
                description = "Long description.";
                hasDate = 1;
                name = Apollo;
                scene = "Store Scene";
                weekDay = Thursday;
                youtubeVideo = "http://www.youtube.com/watch?v=EFo2zQL1gEs";
            },
                    {
                date = "2011-02-04 20:09:40 +0000";
                description = "Long description.";
                hasDate = 1;
                name = Aqua;
                scene = "Store Scene";
                weekDay = Thursday;
                youtubeVideo = "http://www.youtube.com/watch?v=5LRb4C-3tJQ";
            }
        );
     
  15. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #15
    Ah, so each section still has an array (just of dictionaries). Whew! Thought you were suggesting you had no arrays at all. It's certainly possible but, perhaps, just not overly usable.
     
  16. SimonBS thread starter macrumors regular

    SimonBS

    Joined:
    Dec 30, 2009
    #16
    Oh, yes. I do use an array :)

    I played a bit with storing it all in one dictionary or array as I have beensuggested and found this to be a good and easy way. It might not be the best, I'm sure it isn't but it's certainlyneasy to work with.
     
  17. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #17
    I think in terms of sectioned-table datasource modeling, there is no "best". As is often the case with programming solutions, there are numerous solutions that achieve the same end. :)
     
  18. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #18
    One of the questions about the data model is: how simple is it to get the data for a specified row? For a non-sectioned table view it's simply:

    Code:
    NSDictionary* rowDictionary = [dataList objectAtIndex:indexPath.row];
    
    And of course number of rows and number of sections is equally simple.

    For a sectioned table view using the "array of arrays of dictionaries" design, to get the rowData is:

    Code:
    NSArray* sectionArray = [dataList  objectAtIndex:indexPath.section];
    NSDictionary* rowDictionary = [sectionArray objectAtIndex:indexPath.row];
    
    and number of sections and number of rows is pretty simple too.

    @Simon, How do you get the rowDictionary for your data model?
     
  19. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #19
    Yes, but then you add the wrinkle of section titles and how to store those... ;)
     
  20. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
  21. SimonBS thread starter macrumors regular

    SimonBS

    Joined:
    Dec 30, 2009
    #21
    I get it from a plist.

    Code:
    // load plist
    	NSString *path = [[NSBundle mainBundle] pathForResource:@"Bands" ofType:@"plist"];
    	NSMutableArray* tmpArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
    	
    	// sort array after name ascending
    	NSSortDescriptor *nameSorter = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    	[tmpArray sortUsingDescriptors:[NSArray arrayWithObject:nameSorter]];
    Then I run through the tmpArray to create the sections dictionary

    Code:
    // create the index
        for (int i = 0; i < [tmpArray count]; i++){
    		char alphabet = [[[tmpArray objectAtIndex:i] objectForKey:@"name"] characterAtIndex:0];
            NSString *uniChar = [NSString stringWithFormat:@"%C", alphabet];
    		
    		NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.name beginswith[c] %@", uniChar];
    		NSArray *bandsThisAlphabet = [tmpArray filteredArrayUsingPredicate:predicate];
    		
    		[sections setObject:bandsThisAlphabet forKey:uniChar];
        }
     

Share This Page