Confusion about the para indexPath of cellForRowAtIndexPath in iOS - ios

I'm new to iOS dev so this might be a simple question to you but I check the apple dev guide just could not find it.
I have many UITableViews using one dataSourceDelegate,this is how I create them:
- (void)createSomeTableview:(NSInteger)numberOfTableViews{
tableViewsArray = [[NSMutableArray alloc] initWithCapacity:numberOfTableViews];
for (int i = 0; i < numberOfTableViews; i++) {
UITableView *itemTableView = [[UITableView alloc] initWithFrame:CGRectMake(90, 0, 235, self.view.bounds.size.height) style:UITableViewStylePlain];
[itemTableView setDataSource:self];
[itemTableView setDelegate:self];
[itemTableView registerClass:[KKYItemListCell class] forCellReuseIdentifier:[NSString stringWithFormat:#"%ld", (long)currentSection]];
[tableViewsArray addObject:itemTableView];
[[self view] addSubview:itemTableView];
}
}
I call createSomeTableview when I finish got my data back from my server,then I use a cycle to reload the data of each tableView (I call this in a block of AFNetwoking POST after got the response successfully):
for (int i = 0; i < sectionNum;i++){
currentSection = i;
[[tableViewsArray objectAtIndex:i] reloadData];
}
This is where the disaster came!(T T)There is always a null cell return from the reloading process so I track the reloading process and found a strange thing:
Once I call the method reloadData,the method numberOfRowsInSection is called (I have just one section for each table),after this then go back to call the reloadData of the next Tableview in tableViewsArray.(I used to think the reloadData will call cellForRowAtIndexPath next).
After all the views in tableViewsArray calling numberOfRowsInSection,the method cellForRowAtIndexPath had been called by the last view in tableViewsArray.Well that's fine,it starts to build the data for my tableview~,I do this to specify my tableViews in array:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
.....
if (tableView == sectionTableView) {
...
} else if (tableView == [tableViewsArray objectAtIndex:(long)currentSection]) {
...//data handle
if (itemsNum == 0) { //itemsNum is the count of current table's items
currentSection--; //if loaded end then go to the next tableView
return cell;
}
itemsNum--;
return cell;
}
return cell;
}
But something wrong with it!!! I got 18 items in the current data from server (itemsNum = 18),and I did get 18 return from numberOfRowsInSection.But when the indexPath.row == 6,the tableView's index is currentSection - 1,which means it's no longer the current tableView (actually it was the next tableView)is calling the method `cellForRowAtIndexPath'.
So (tableView == [tableViewsArray objectAtIndex:(long)currentSection]) is false,method return a nil cell,then crashed.
I'm wondering where does the method 'tableView:cellForRowAtIndexPath' get the para row in indexPath?Isn't the return of 'numberOfRowsInSection'?(not seemed like in my program).

Related

What causes outOfBounds error in cellForRowAtIndexPath?

I'm having the following issue raised by Crashlytics :
[__NSArrayM objectAtIndexedSubscript:]: index 5 beyond bounds for empty array
-TopicListViewController tableView:cellForRowAtIndexPath:]
While accessing the dataSource with indexPath.row.
We have some asynchronous data update updating the datasource, and that variable is nonatomic.
Would it be possible that cellForRowAtIndexPath is called while the dataSource is being updated? Hence causing to access an index that doesn't exist anymore?
Can it be because the variable is nonatomic?
Here's the relevant code :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
if (indexPath.row > [self.tableData count] - 1 || ![self.tableData isValidArray]) {
return nil; //Some protection to prevent this issue...
}
TopicCell * cell = (TopicCell *)[tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
cell.delegate = self;
NSDictionary * data = nil;
if (self.we_isSearching) {
data = self.we_searchResult[indexPath.row];
} else {
data = [self.tableData objectAtIndex:indexPath.row]; //Crashes here
}
"index 5 beyond bounds for empty array" simply states that either you didn't initialise your array or the range of the value you're accessing is out of the range of the array. You are trying to access index 5 in an empty/having less element array, or non initialised array that's why it is giving you "outOfBounds" in cellForRowAtIndexPath.
Would it be possible that cellForRowAtIndexPath is called while the dataSource is being updated?
Yes, cellForRowAtIndexPath will always be called when you're going to see a new tableview cell for example when you're scrolling the tableview Or in case you've added some kind of notification added to your datasource or by reloading the tableview.
You can put a break point at cellForRowAtIndexPath and check the stack trace maybe you get something that causes the tableview to reload.
Try to count from self.tableData in return of numberOfRowsInSection methods. Like
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.tableData count];
}
pass array count in numberOfRowsInSection of tableView method.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return array.count;
}
Your condition
if (indexPath.row > [self.tableData count] - 1 || ![self.tableData isValidArray])
is wrong. If there is 5 elements, the last indexPath.row will be index 4 so condition with real values will be:
if (4 > 5 - 1) --> if 4 > 4
So the valid condition is:
if (indexPath.row >= [self.tableData count] - 1)
But with correct condition you will have crash on:
return nil
Because obvisously your data source is different than table data source. Your model data source should be always same as table data source.

cellForRowAtIndexPath not executing for all Custom View Cells from NIB file

I'm having an issue with running two TableViews in the one ViewController.
EDIT:
To make the question a bit more understandable, I've placed the snippet below of the code.
Things that may be relevant:
Both use custom cells.
From what I have done with the debugger, it looks at though it doesn't pass through the following if statement:
if (tableView == self.tblFriendsList){...}
3.Initially I was trying to use a Containment View to add the second UITableViewController, but now I have opted to try using a UITableView.
The containment view (which includes the two tableViews) comes from a NIB.
I have tried both adding the delegate and datasource manually, and through the file's owner.
I have changed the code so that if it doesn't execute the if statement, then it will execute what is required for the other table (which is setting up successfully), but previously it was also working for the first table when I used the example that I have written below. It is the other table that fails to load when both when used as a tableView or when I try adding an entire ViewController to a subview (I've been interchanging between both to try to solve this).
Spelling has been checked on the actual code many-a-time, so I'm more thinking that it may have to do with the fact that it isn't entering the if statement for the one mentioned above.
Most examples I have seen from videos/Stack Exchange have mainly been prior to this year, so I am wondering if there has been an update to practical coding that I have missed.
Sadly I am only doing this for a job for someone wanting an exact replica of their design, or I would have thought of a nicer alternative!
I have attempted all answers so far, but have come up short... although I have seen too many tutorials on this that have succeeded, so I am very unsure about something funny going on...
EDIT AGAIN 11. Even when setting the tableView tag and running an if statement, the debugger still shows the code skipping past the if statement. Even when the reload data method is called, the debugger does not have another break when it should.
Currently, the code sits as this:
For viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.m_arrVenueList=[[NSMutableArray alloc]init];
self.m_dictCells = [[NSMutableDictionary alloc] init];
[self setNavigationProperties];
[self loadFriendByLocation];
[self setRefreshController];
self.m_dictFriendListCells = [[NSMutableDictionary alloc] init];
self.tblFriendLocation.delegate = self;
self.tblFriendLocation.dataSource = self;
self.tblFriendsList.delegate = self;
self.tblFriendsList.dataSource = self;
[self.tblFriendsList reloadData];
}
For numberOfRowsInSection:
Note: Just one section for each.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.tblFriendsList) {
return [self.arrFriendsList count];
}
// Return the number of rows in the section.
return [self.m_arrVenueList count];
}
For cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.tblFriendsList) {
NSDictionary *friends=[self.arrFriendsList objectAtIndex:indexPath.row];
NSString *friendID = [Utility formattedValue:[friends objectForKey:#"user_id"]];
// Configure the cell using friend object...
NSString *sIdentifier = [NSString stringWithFormat:#"TableCell_%ld",(long)friendID];
FriendListVenueCell *cell = (FriendListVenueCell *)[self.m_dictCells objectForKey:sIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
if (cell == nil)
{
cell = (FriendListVenueCell*)[Utility getViewFromXib: #"FriendListVenueCell" classname:[FriendListVenueCell class] owner:tableView];
if (indexPath.row%2 != 0)
{
cell.contentView.backgroundColor = UIColorWithRGB(11,21,39);
}
else
{
cell.contentView.backgroundColor = UIColorWithRGB(12,23,32);
}
}
[cell setCellDataForFriend:friends];
[self.m_dictCells setObject:cell forKey:sIdentifier];
return cell;
}
// Configure the cell using friend object...
NSString *sIdentifier = [NSString stringWithFormat:#"TableCell_%ld",(long)indexPath.row];
FriendsAtVenueCell *cell = (FriendsAtVenueCell*)[self.m_dictCells objectForKey:sIdentifier];
if (cell == nil)
{
cell = (FriendsAtVenueCell *)[Utility getViewFromXib:#"FriendsAtVenueCell" classname:[FriendsAtVenueCell class] owner:tableView];
cell.m_FriendViewContrller = self;
[self.m_dictCells setObject:cell forKey:sIdentifier];
if (indexPath.row%2 != 0)
{
cell.contentView.backgroundColor = UIColorWithRGB(11,21,39);
}
else
{
cell.contentView.backgroundColor = UIColorWithRGB(12,23,32);
}
}
cell.delegate=self;
Venue *tempDic=(Venue*)[self.m_arrVenueList objectAtIndex:indexPath.row];
[cell setCellData:tempDic];
if (tempDic.nVenueId == 0)
{
[cell.m_imgLogo setHidden:YES];
[cell.m_imgLogobg setHidden:YES];
[cell.m_btnFollow setHidden:YES];
}
cell.btnFifthImg.tag = indexPath.row;
cell.m_btnFollow.tag = tempDic.nVenueId;
cell.m_RowId= indexPath.row;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
So far, what I have attempted to do - from what I have read from StackExchange and seen on tutorials - is to have the method cellForRowAtIndexPath have separate cases for each cell and then return that cell.
Examples include
if (tableView == oneTableViewOutlet) {
//load cell
return cell;
} else if (tableView == anotherTableViewOutlet) {
//load cell
return cell;
}
Hopefully this hasn't made the question too long, I am trying to condense it!
EDIT FINISHED
I've watched it with success on a few videos where they have added the delegate and datasource to self for each respective TableView outlet and it has worked from there, but I am having the issue of one outlet loading and not the other.
The above image has me debugging the issue, where I have Stepped Through the execution of the function and it has successfully initiated the first case, but then returns from the function and does not execute for the other TableView.
The code is tricky as I have custom cases in it, but if there are any other thoughts on debugs or references to help that would be appreciated.
In summary:
2 TableViews
Both hooked up to delegate and datasource
2 Custom NIB Cells used for each table
Only one executing
Add
NSLog(#"Table:%#", tableView) at Line 204.
See if it prints two different tableViews.
and Try replacing Line 205 with if([tableView isEqual:self.tblFriendLocation])
Set the tag for your tableview and compare with tag
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
TablevIewCell1 *cell1=[tableView dequeueReusableCellWithIdentifier:#"CellId1"];
TablevIewCell2 *cell2=[tableView dequeueReusableCellWithIdentifier:#"CellId2"];
switch (tableView.tag) {
case 1:
[cell1.textLabel1 setText:[self.arrForTableView1 objectAtIndex:indexPath.row]];
return cell1;
break;
case 2:
[cell2.textLabel2 setText:[self.arrForTableView2 objectAtIndex:indexPath.row]];
return cell2;
break;
default:
break;
}
return nil;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if (tableView ==self.tableView1)
return self.arrForTableView1.count;
else
return self.arrForTableView2.count;
}
or else
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
TablevIewCell1 *cell1=[tableView dequeueReusableCellWithIdentifier:#"CellId1"];
TablevIewCell2 *cell2=[tableView dequeueReusableCellWithIdentifier:#"CellId2"];
if ([tableView isEqual:self.tableView1]) {
[cell1.textLabel1 setText:[self.arrForTableView1 objectAtIndex:indexPath.row]];
return cell1;
} if ([tableView isEqual:self.tableView2]){
[cell2.textLabel2 setText:[self.arrForTableView2 objectAtIndex:indexPath.row]];
return cell2;
}
return nil;
}
Hope this will help you.
if([tableview isEqual:tblName])
First of all this is how you must check equality. Secondly post all methods so that one can see exactly what is wrong with your code.

How Can I Change Tableview Array with Segment Control

I Have a one table view and i have two array. My arrays name AllItems and SpecialItems. I Use segment control. I wantto if segment value is 0 tableview load AllItems Array, When change segment value and value is = 1 than mytableview reload tada but SpecialItems array. Can u help me please. Thanks.
I solved this problem with table tag.
- (IBAction)segmentControlChanged:(UISegmentedControl *)sender {
if (sender.selectedSegmentIndex == 1) {
mytable.tag = 1;
}
else
{
mytable.tag = 0;
}
[mytable reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(tableView.tag==1)
{
return [specialItems count];
}
else
return [allItems count];
}
You could create two data source classes that implement all the UITableViewDataSource methods: one for AllItems and one for SpecialItems. To switch between the two, connect a valueChanged action. In the method that is called, set the data source and reload the table view.
- (void)valueChange:(UISegmentedControl *)sender
{
if (/* condition for all items */) {
self.tableView.dataSource = self.allItemsDataSource;
} else {
self.tableView.dataSource = self.specialItemsDataSource;
}
[self.tableView reloadData];
}
I would personally create an array which the data is loaded from. Put this in your implementation:
NSArray * _tableData
Then in your viewDidLoad just allocate this for the array which we want it to start on.
_tableData = [[NSArray alloc] initWithArray:allItems];
This initially loads the data we will always see as the segment control starts on index 0. We have to set the initial data somewhere so the tableView loads with some data in it.
Then set the number of rows and the cellForRowAtIndex to pick up from the _tableData array
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _tableData.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView_ cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell * cell = [tableView_ dequeueReusableCellWithIdentifier:bCell];
// Here we use the specific array as we would normally
return cell;
}
This step means the tableView will load with the array. Even if the array is empty the view will still load as the number of cells will be zero.
Now in our value changed function we can reset the array as we need to:
- (IBAction)segmentControlChanged:(UISegmentedControl *)sender {
if (sender.selectedSegmentIndex == 1) {
_tableData = allItems;
}
else {
_tableData = specialItems;
}
[self.tableView reloadData];
}
You just need to make sure the segment control changed is linked up in the XIB file (or programatically) and that you reload the table after choosing the array.
This kind of thing is actually really easy to do. I would definitely recommend working it through step by step if you're having trouble. Make sure each step is working before applying the next:
Get the tableView loading with both sets of data individually
Confirm that the segment control is calling the change function when clicked
Then that should do it

UITableView calling delegate method when datasource is empty

I've got my cellForRowAtIndexPath delegate method defined as so:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
PLOTCheckinTableViewCell *cell = (PLOTCheckinTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CheckinCellIdentifier forIndexPath:indexPath];
if([self.items count] == 0){
return cell;
}
NSDictionary *checkin = self.items[indexPath.row];
// configure and return custom cell
}
I'm using a custom cell class (PLOTCheckinTableViewCell).
I faced an issue where the user would pull to refresh and then attempt to pull again before the first request had completed (on completion of the request, I reload the table data). When they did this, the app would crash and say that indexPath.row was basically out of bounds, ie. the array was empty.
By putting in this IF check above, I mitigated the crash.
However,
Why exactly does my IF check "work", I see no visual implications of returning the cell before it's been configured. This is confusing
Are there any better ways to guard against this happening (ie. the table data being reloaded with an empty array)? Surely the numberOfRowsInSection would have returned array count which would be 0? (if it was an empty array)
EDIT (further code)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
float count = [self.items count];
return count;
}
- (void)resetData {
self.items = [NSMutableArray array];
}
-(void) refreshInvoked:(id)sender forState:(UIControlState)state {
[self resetData];
[self downloadHomeTimeline];
[self.refreshControl endRefreshing];
}
- (void)downloadHomeTimeline {
[self.apiClient homeTimeline:self.page completionBlock:^(NSDictionary *data){
for (NSDictionary *obj in data[#"items"]) {
[self.items addObject:obj];
}
[self.itemsTableView reloadData];
}];
}
I couple of things that i would suggest to do. Make sure that the [self.itemsTableView reloadData] is executed on the main thread and also i would put the [self.refresControl endRefreshing] in the completion block. This way it will stop the refresh when its done and you should not let the user more then once simultaneously.
- (void)downloadHomeTimeline {
[self.apiClient homeTimeline:self.page completionBlock:^(NSDictionary *data){
for (NSDictionary *obj in data[#"items"]) {
[self.items addObject:obj];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.itemsTableView reloadData];
[self.refreshControl endRefreshing];
});
}];
}
Also in the numberOfRowsInSection just return count
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.items count];
}
To add to the answer. You should not reset the array before you receive new data. While getting new data the user can still scroll the table and that means new cells will be created but your NSMutableArray doesn't have any data. That is when you get the error and app crashes. You would have to [tableView reloadData] to clear the table so that the tableView would know that there are 0 rows, which i don't think is your intent.
Let me know if that's solves the issue.

UISwitch and viewWithTag is nil in numberOfRowsInSection

I am trying to figure out the best approach for adding and managing a UISwitch to my UITableView's headers, and I've read several links so far on how best to use the UISwitch in a table: here, here and here. These links demonstrate using the UISwitch in the cell whereas I'm using them in a custom UIView for the header. That said, I would prefer to use tags to manage these objects but I'm not able to figure out why the viewWithTag approach is not working.
Side note: I was able to put the UISwitches into an NSMutableArray at run time and manage them that way, but I'd rather not be so verbose, manage bounds violations/indexes on the array or check for nil lists, etc... It is also not clear to me how I would do this using IBOutlets. This is why I'm trying the tag approach.
My goal is to use the switches to collapse / expand the rows in each of the sections which is why I've thought to tag and add UISwitches as sub views to the UIView that I return in viewForHeaderInSection. Then re-reference them later when I need to perform some logic to collapse the cells. Additionally, at run time I could have 1 or more sections so hard-coding the tag numbers is impractical. Here's the code for that method:
Assuming:
#define kSwitchTagRange 2000
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, 44)];
// Add UISwitches and ensure that UISwitch doesn't already exist for this section...
UISwitch *switchView = (UISwitch*)[self.tableView viewWithTag:(kLayerSwitchTagRange + section)];
if (switchView == nil) {
// Create switch
switchView = [[UISwitch alloc] init];
double xOffset = self.tableView.bounds.size.width - switchView.frame.size.width;
switchView.frame = CGRectMake(xOffset,
7,
switchView.frame.size.width,
switchView.frame.size.height);
[switchView addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
// Should we tag the switch view?
switchView.tag = kSwitchTagRange + section;
}
// Add switch as subview
[view addSubview:switchView];
return view;
}
In switchChanged: I just reload the table's data:
- (void)switchChanged:(id)sender {
[self.tableView reloadData];
}
Finally as the table's data is recreated I attempt to retrieve the UISwitch and determine it's on/off state, then I return 0 for the number of rows in the section if OFF and some other number if ON:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger rowCount = 0;
UISwitch *switchView = (UISwitch*)[self.view viewWithTag:(kSwitchTagRange + section)];
if (switchView != nil && switchView.isOn == NO)
{
rowCount = 0;
}
else
{
// Row count is something other than 0
rowCount = 3;
}
return rowCount;
}
Unfortunately, this switchView is always nil.
I have a few guesses, but can't determine why this is happening.
Guess 1: The UIView to which the switch I want was added is deallocated when the table's data was reloaded. It doesn't exist in memory and therefore can't be found searching by tag.
Guess 2: I'm adding the UISwitch to the view object incorrectly (in many examples I've seen objects added to a UITableViewCell's contentView, however since I'm sending back a UIView in the viewForHeaderInSection: method, I'm not sure those examples apply here. addSubview should add any object to the tree.
Can anyone tell me why the viewWithTag method above would return nil? I'm leaning toward Guess #1 but haven't found any documentation that tells me the custom header UIView is deallocated at any time. The cells are re-used, but what about the section headers?
Lastly, I've read this post and while the recommendation on tag-use makes sense it seems to dislike the tag approach at all. Why not use tags if you don't find using them to be messy and they're being used intelligently? Why is the tag feature even available?
Really, I just want to know why the viewWithTag method returns nil in my case.
Cheers!
I'm not sure why your viewWithTag always returns nil, I do think it might have to do with the fact that the views are deallocated at some point.
However, the tags can still work to do what you want, but you need to have a property or key in your model object that keeps track of the value of the switch. You can use the tag to tell which section the switch is in in its action method, and update the model appropriately. Here's what I did. My model is an array of dictionaries where the value of the key "rowData" is an array with all the data for that section, and the value of the key "switchValue" is an NSNumber,0 or 1, representing the state of the switch. So, my data looks like this:
2012-11-28 22:50:03.104 TableWithSwitchesInHeaderView[3592:c07] (
{
rowData = (
One,
Two,
Three,
Four
);
switchValue = 1;
},
{
rowData = (
Red,
Orange,
Yellow,
Green,
Blue,
Violet
);
switchValue = 1;
},
)
This is what I have in the relevant table view delegate and data source methods:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.theData.count;
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, 44)];
UISwitch *switchView = [[UISwitch alloc] init];
double xOffset = self.tableView.bounds.size.width - switchView.frame.size.width;
switchView.frame = CGRectMake(xOffset,7,switchView.frame.size.width,switchView.frame.size.height);
[switchView addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
switchView.tag = kSwitchTagRange + section;
switchView.on = [[[self.theData objectAtIndex:switchView.tag - 101] valueForKey:#"switchValue"] boolValue];
[view addSubview:switchView];
return view;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 50;
}
- (void)switchChanged:(UISwitch *)sender {
NSMutableDictionary *dict = [self.theData objectAtIndex:sender.tag - 101];
[dict setValue:[NSNumber numberWithBool:sender.isOn] forKey:#"switchValue"];
[self.tableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
BOOL switchValue = [[self.theData[section] valueForKey:#"switchValue"] boolValue];
if (switchValue) {
return [[self.theData[section] valueForKey:#"rowData"] count];
}else{
return 0;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text = [[self.theData[indexPath.section]valueForKey:#"rowData"]objectAtIndex:indexPath.row] ;
return cell;
}
My kSwitchTagRange was #defined as 101. This code worked to collapse the rows (actually get rid of all of them) in any section where the switch was off.
It seems from the post here that the UIView returned by viewForHeaderInSection is deallocated and must be recreated (as well as all it's subviews, including my UISwitch), rather than re-used. I trust that Apple assumes that you'll have far fewer section headers than you are cells and therefore didn't provide a reusability mechanism for retrieving section headers. The data-bound approach proposed by #rdelmar seems to make sense for this scenario.

Resources