Customize a data-driven TableView - iphone

I have a grouped tableview that is populated with XML data in one section. What I would like to do is create another section prior to the data driven one, and apply an action to it.
Example:
The user is presented with a button that says "use your current location" (manually created section) and below that is a list of countries the user can alternatively choose from choose from (data driven section)
Use the settings menu as a guide. There are some options which are a single row in a section, so they appear to be a button...
If this doesn't make sense, I will try to explain it better.
So I have these two obvious lines of code...simple enough
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [countrysData count];
}
What I would like is to have numberOfSectionsInTableView return 2 and have the first "Section" say "Click to use your current location" which would then push into view a map, and the second section display the list of countries I currently have working.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(section == 0){
return 1;
}else{
return [countrysData count];
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2;
}
then you should choose what to do in your didSelectRowAtIndexPath: method due to the indexPath.section. oh, and you should check indexPath.section in your cellForRowAtIndexPath: method.

You just need to update all of your implementations of the UITableViewDataSource and UITableViewDelegate protocols to appropriately account for the new section and its row(s).
For example, here's how to update numberOfSectionsInTableView:, tableView:numberOfRowsInSection:, and tableView:cellForRowAtIndexPath:, but you will want to update at least tableView:didSelectRowAtIndexPath: as well:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// calculate the number of sections of non-data (might just be 1)
// calculate the number of sections for the data (you were already doing this, might just be 1)
// return the sum
return 1 + 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger rowCount = 0;
switch (section) {
case 0:
// non-data section has 1 row/cell
rowCount = 1;
break;
case 1:
// data section uses an array
rowCount = [dataArray count];
break;
}
return rowCount;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *nonDataCellID = #"NonDataCell";
static NSString *dataCellID = #"DataCell";
UITableViewCell *cell;
int section = [indexPath indexAtPosition:0];
int row = [indexPath indexAtPosition:1];
switch (section) {
case 0:
// or you can just use standard cells here
cell = [tableView dequeueReusableCellWithIdentifier:nonDataCellID];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"NonDataCell" owner:self options:NULL];
cell = nonDataCell; // nonDataCell is an IBOutlet to this custom cell
}
// configure non-data cell here (use tags)
UILabel *someLabel = (UILabel*)[cell viewWithTag:1];
someLabel.text = #"Non-data cell";
break;
case 1:
// or you can just use standard cells here
cell = [tableView dequeueReusableCellWithIdentifier:dataCellID];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"dataCell" owner:self options:NULL];
cell = dataCell; // dataCell is an IBOutlet to this custom cell
}
// configure data call here (using "row")
UILabel *someDataLabel = (UILabel*)[cell viewWithTag:1];
someDataLabel.text = [[dataArray objectAtIndex:row] valueForKey:#"name"];
break;
}
return cell;
}

I'm pretty sure you can alter the return of UITableViewDataSource's method 'numberOfSectionsInTableView:' on the fly. Once the user selects the choice of an additional section, just set a flag to have the method return the number of tables you want. Then you force a reload of the table and you should see the new section.

Related

Populating UITableView from NSArray with indexPath.row

I am trying to populate
UITableView in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method with an array I have initialized in viewDidLoad method.
self.questionsArray = [NSArray arrayWithObjects:#"Q1",#"Q2",#"Q3",#"Q4",#"Q5",#"Q6",#"Q7",#"Q8",#"Q9",#"Q7",#"Q8",#"Q9",#"Q10",#"Q11"];
I understand how to implement various sections and reusing a cell with an identifier. As most of the examples I have seen, I tried like question.text = [NSString stringWithFormat:#"%d. %#", indexPath.row ,[self.questionsArray objectAtIndex:indexPath.row]];
But I am getting like
0. Q1 1. Q2 2. Q3 3. Q4 4. Q5 5. Q6 0. Q1 2. Q2 ....
Please help to load all questions (NSString) to my TableView. I messing up with indexPath.row. Its very difficult to understand. Any tips for best practices to handle indexPath effectively.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
switch (section) {
case 0:
return [self.questionsArray count];
case 1:
return 1;
default:
return 0;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0) {
static NSString *QuestionCellIdentifier = #"QuestionCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:QuestionCellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"QuestionCell" owner:self options:nil];
}
cell = questionCell; // IBoutlet
self.questionCell = nil;
}
question.text = [NSString stringWithFormat:#"%d. %#", indexPath.row ,[self.questionsArray objectAtIndex:indexPath.row]];
return cell;
}
else if (indexPath.section == 1){
static NSString *CustomCellIdentifierMore = #"CustomCellIdentifierMore";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CustomCellIdentifierMore];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CustomCellIdentifierMore] autorelease];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
switch (indexPath.row) {
case 0:
cell.textLabel.text=#"Select";
break;
default:
break;
}
}
return cell;
}
return nil;
}
cellForRowAtIndexPath is not guaranteed to load cells in order and will be called to update certain cells. You also need to provide the numberOfRowsInSection method, for example:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.tableData count];
}
Are your cells displaying properly or is it just the nslog that is producing confusing output?
Try calling reloadData on your tableView after creating questionsArray in viewDidLoad, so like this:
self.questionsArray = [NSArray arrayWithObjects:#"Q1",#"Q2",#"Q3",#"Q4",#"Q5",#"Q6",#"Q7",#"Q8",#"Q9",#"Q7",#"Q8",#"Q9",#"Q10",#"Q11"];
//or whatever your property for the tableview is called.
[self.tableView reloadData];
Your cellForRowAtIndexPath implementation is a little funky. First, the curly brackets for indexPath.section == 0 condition don't match. Second, I don't see where you are setting the text for cell that you are returning. Whatever you are assigning to question.text should be assigned to cell.textLabel.text. Third, for indexPath.section == 1 condition, you are only setting the text when the cell is nil. The table view is pretty smart about cell management. When a cell goes out of view due to scrolling, it will reuse the cell instead of entirely creating a new one. So, whenever you deque a cell, it may not always be nil. So, you should be setting the cell text outside the if (cell == nil) loop.

Combine static and prototype content in a table view

Is there a way to combine static tableview cells (static content) with dynamic tableview cells (prototype content) using storyboard?
I suggest you treat your table as dynamic, but include the cells you always want at the top. In the Storyboard, place a UITableViewController and have it use a dynamic table. Add as many UITableViewCell prototypes to the table as you need. Say, one each for your static cells, and one to represent the variable cells.
In your UITableViewDataSource class:
#define NUMBER_OF_STATIC_CELLS 3
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.dynamicModel count] + NUMBER_OF_STATIC_CELLS;
}
and, then
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row < NUMBER_OF_STATIC_CELLS) {
// dequeue and configure my static cell for indexPath.row
NSString *cellIdentifier = ... // id for one of my static cells
} else {
// normal dynamic logic here
NSString *cellIdentifier = #"DynamicCellID"
// dequeue and configure for [self.myDynamicModel objectAtIndex:indexPath.row]
}
}
I had a problem, although it was a slight variant of this. I actually wanted to mix dynamic and static cells but in different groups. Meaning group 1 would have static only cells and group 2 would have dynamic cells.
I accomplished this by actually hard coding static cell values (based on their prototype cell identifiers). The dynamic sections would have normal dynamically populated content. Here is some example code in case anyone else has the same issue:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (section == 1){
return #"Dynamic Cells";
}
if (section == 0){
return #"Static Cells";
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0) {
return 1; //However many static cells you want
} else {
return [_yourArray count];
}
}
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
if (indexPath.section == 0) {
NSString *cellIdentifier = #"staticCellType";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = #"some static content";
return cell;
} else if (indexPath.section == 1){
NSString *cellIdentifier = #"dynamicCellType";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = [_yourArray objectAtIndex:indexPath.row];
return cell;
}
return nil;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2;
}
Since no one has actually provided a real answer to the problem (using both static and prototype cells in the same table view), I figured I'd chime in.
It can be done!
Create your static cells as you see fit.
For the sections that need a dynamic cell, if you are NOT using standard UITableViewCell type, you'll need to create your custom one in a separate Nib, otherwise you can use the standard ones.
Then implement the following delegates. Basically for each of these delegates, for the static stuff we want to call super, for the dynamic, we return our values.
First, IF you need to selectively show your dynamic section, you'll want to implement the numberOfSectionsInTableView (otherwise you can leave this delegate out):
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
int staticSections = 1;
int dynamicSections = 1;
if (SOME_BOOLEAN) {
return staticSections + dynamicSections;
} else {
return staticSections;
}
}
Then, you need to implement numberOfRowsInSection:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 1) {
return A_COUNT;
} else {
return [super tableView:tableView numberOfRowsInSection:section];
}
}
Then, you need to implement heightForRowAtIndexPath:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 1) {
return 44.0f;
} else {
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
}
Then indentationLevelForRowAtIndexPath:
- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 1) {
return 1; // or manually set in IB (Storyboard)
} else {
return [super tableView:tableView indentationLevelForRowAtIndexPath:indexPath]; // or 0
}
}
Finally, cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 1) {
SomeObject *obj = self.someArray[indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"DynamicCell" forIndexPath:indexPath];
cell.textLabel.text = obj.textValue;
return cell;
} else {
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}
}
You can always make one you your tableviews appear similar to the static table but define it in code. Set the sections, amount or rows per section, headers etc. through the delegate methods.
Unfortunately, this is not possible since static table views must be in a UITableViewController and that only allows one tableview.
What you need to do is make three more dynamic UITableviewCell's and load them individually for the first three rows where you want the static content.
If you aren't sure how to do this, let me know and I can find some code.
You can't have one tableview be static and the other dynamic in the same view controller so you will need to make them both dynamic. In the first tableview you will configure the cells in code on initializing the view controller never update them.
Add a UIViewController to your storyboard.
Add two Table Views (Not
TableViewControllers) to the UIView Controller.
Select each tableView and configure both for dynamic cells.
Build and attach your view controller. 2 tableview on a single view explains that step.
As another option you can achieve a similar look by embedding your dynamic tableview in part of a view similar to the link in step 4 and then do whatever you wanted to in the rest of the view to setup what you were planning to do with static cells by using scrollviews, labels, and buttons.
You could also create buttons (one for each static cell you have) that are styled like your cells and place them in the tableHeaderView or tableFooterView of the UITableView; those buttons are just views after all.
You'll need to add some logic for making selections on the buttons vs. the cells so it maintains the usual look and feel.
Of course, this assumes that you want to insert static cells into your table view at the top or bottom of the table.
One way to have dynamic content in a static table view is to clone cells where additional rows are needed.
For the dynamic section of my table view, I lay out one or more cells in Interface Builder. At runtime, I can clone those by archiving using NSCoder and then unarchiving.
It works, but is not necessarily prettier than starting with a dynamic prototype table view and creating static rows from there.
It fails with standard table view cells. The lazily created text labels are not laid out correctly. Hence I used UITableViewCell subclasses where I take care of archiving and unarchiving subviews.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == kContactsSection) {
NSArray *contacts = self.contacts;
Contact *contact = [contacts objectAtIndex:indexPath.row];
NSString *name = contact.name;
NSString *role = contact.role;
if ([role length] == 0) {
NNContactDefaultTableViewCell *cell = (id)[tableView dequeueReusableCellWithIdentifier : #"contactDefault"];
if (cell == nil) {
NNContactDefaultTableViewCell *template = (id)[super tableView : tableView
cellForRowAtIndexPath :[NSIndexPath indexPathForRow:0 inSection:kContactsSection]];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:template];
cell = [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
cell.contactTextLabel.text = name;
return cell;
}
else {
NNContactDetailTableViewCell *cell = (id)[tableView dequeueReusableCellWithIdentifier : #"contactDetail"];
if (cell == nil) {
NNContactDetailTableViewCell *template = (id)[super tableView : tableView
cellForRowAtIndexPath :[NSIndexPath indexPathForRow:1 inSection:kContactsSection]];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:template];
cell = [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
cell.contactTextLabel.text = name;
cell.contactDetailTextLabel.text = role;
return cell;
}
}
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}
In the above example I have two cell types. Both laid out in Interface Builder as part of a static table view.
To get dynamic content in one section, I also need to override the following methods:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == kContactsSection) {
NSArray *contacts = self.contacts;
NSUInteger contactCount = [contacts count];
return contactCount;
}
return [super tableView:tableView numberOfRowsInSection:section];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger section = indexPath.section;
NSInteger row = indexPath.row;
if (section == kContactsSection) {
return [super tableView:tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:kContactsSection]];
}
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
- (CGFloat)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger section = indexPath.section;
if (section == kContactsSection) {
CGFloat indentation = [super tableView:tableView indentationLevelForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:kContactsSection]];
return indentation;
}
CGFloat indentation = [super tableView:tableView indentationLevelForRowAtIndexPath:indexPath];
return indentation;
}

Breaking a table into custom sections for iphone application (xcode 4.2)

I would like to break my table view into sections. I would like to see an example, of a table view broken into 3 sections, and then me being able to choose the index where the sections begin.
So if I have an array of objects, and they populate a table view. I would like to choose the titles of the sections and where the sections begin (so for row 1-13 would be section 1, 13-30 would be section 2, etc...).
I have this so far:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
if (ingredientListChoice == 1) {
return 3;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (ingredientListChoice == 1) {
return #"Section Title";
}
}
Please let me know if you can show me an example of what I am getting at. Thank you.
Here's a rough way to do it. Basically you'll need to return the correct size of each section from tableView:numberOfRowsInSection: and then set the correct content in your table cell by adding an offset to the row index position when pulling the content from your data array in tableView:cellForRowAtIndexPath:.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
switch (section) {
case 0: return 13; break;
case 1: return 17; break;
etc...
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
cell = [tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
int offset = 0;
switch (section) {
case 0: offset=0; break;
case 1: offset=13; break;
case 2: offset=30; break;
etc...
}
int arrayRow = indexPath.row + offset;
cell.textLabel.text = [myArray objectAtIndex:arrayRow];
return cell;
}
A cleaner way might be to store the sizes of your sections in an array you store as a property (which you would perhaps set in viewDidLoad) and then numberOfRowsInSection and cellForRowAtIndexPath could read the necessary value(s) from that array, so that if in the future you needed to change the sizes of your sections you'd only have to update one place.

Populate NSMutableArrays to custom sections

Want to populate two NSMutableArrays to 2 Custom Sections of tableView;
I have two NSMutableArrays with events and I want to split them to now and today sections.
For the first section:
I want to remove events from nowEvents Array and place them into my frist section.
EventClass *event = [appDelegate.nowEvents objectAtIndex:indexPath.row];
event.startTime is the start time of my event
event.endTime is the end time of my event
For 2nd section:
Remove the events that are happening now
EventClass *event = [appDelegate.todayEvents objectAtIndex:indexPath.row];
What I'd like to know is numberOfRowsInSection method, how it would look like and cellForRowAtIndexPath (here I've tried NSInteger section = [indexPath section]; if (section == 0) { } if (section == 1) {} - but what about if I won't have an event that is happening now ?)
Something like this should work
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
#warning Incomplete method implementation.
// Return the number of rows in the section.
switch (section) {
case 0:
return [appDelegate.nowEvents count];
case 1:
return [appDelegate.todayEvents count];
default:
return 0;
}
}
- (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];
}
switch (indexPath.section) {
case 0:
EventClass *nowEvent = [appDelegate.nowEvents objectAtIndex:indexPath.row];
//set up cell to display this event
break;
case 1:
EventClass *todayEvent = [appDelegate.todayEvents objectAtIndex:indexPath.row];
//set up cell to display this event
break;
default:
break;
}
// Configure the cell...
return cell;
}
If you don't have an event happening now then your nowEvent array will be empty so in the numberOfRowsInSection will return 0 and therefore the cellForRowAtIndexPath won't be called as there is nothing to display. Hope this helps.
You can have 1 or 2 sections in your table view depending on the case, then in your numberOfRowsInSection you check if there are any events now (if they aren't then you drop that section). So Your ifs would be something like
BOOL eventsNow = YES/NO;
if (section == 0 && eventsNow) { }
if (section == 0 && !eventsNow) { }
if (section == 1 && eventsNow) { }
if (section == 1 && !eventsNow) { /* This case shouldn't happen so assert or throw ...*/ }

Hiding UITableViewCell

Is there a way to hide a UITableView cell? I'm looking for some property or method I can invoke on the UITableViewCell returned by a synchronous cellForRowAtIndexPath() to hide it and make it unselectable by the user.
For me using mapping is not easy way, so I decided to use SAS method. But it doesn't work with my custom cell. So, I correct it:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row == 7 && hide7Row){
UITableViewCell* cell = [cells objectAtIndex:indexPath.row];
cell.hidden = YES;
return 0.0;
}
else if(indexPath.row == 8 && hide8Row){
UITableViewCell* cell = [cells objectAtIndex:indexPath.row];
cell.hidden = YES;
return 0.0;
}
else {
return 44.0;
}
}
Works Fine.
You mean to leave a gap in the table where the cell should be, or just to progress from the one before it straight to the one after it? In the former case, I guess you might try getting the cell's contentView and set its hidden property to YES; otherwise, you'll just have to do a little logic in your -tableView:numberOfRowsInSection: and -tableView:cellForRowAtIndexPath: methods, returning (the number of cells you'd otherwise return - 1) from the first, and, depending on whether the row index is less than or greater than the row you're not including, either (the cell you'd otherwise return) or (the cell at (the row index + 1)), respectively.
(edit, because the explanation was convoluted:)
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
if(section == theSectionWithoutARow)
{
if(shouldRemoveTheRow)
return [theArrayWithTheSectionContents count] - 1;
else
return [theArrayWithTheSectionContents count];
}
// other sections, whatever
}
- (UITableViewCell *)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// blah blah standard table cell creation
id theCellObject;
if(indexPath.section == theSectionWithoutARow)
{
NSInteger theActualRowToDisplay = indexPath.row;
if(shouldRemoveTheRow && indexPath.row >= theRowIndexToRemove)
{
theActualRowToDisplay = indexPath.row + 1;
}
theCellObject = [theArrayWithTheSectionContents objectAtIndex:theActualRowToDisplay];
}
// now set up the cell with theCellObject
return cell;
}
There is no method to do that on the cellForRowAtIndexPath as far as I am aware.
Noah Witherspoon's method seems to be more or less workable, although it will need to be modified if you want multiple rows to be hidden.
Another way to approach it is to create a "cell map", I don't know if this is more efficient or not, but I've used it and it worked.
Let us say you have an NSArray (or mutable version thereof) of data which is to be shown in your TableView. The array's count property is used as the return value for your numberOfRowsInSection delegate method. This is a somewhat typical approach to my knowledge.
To make it so that only some of the rows are shown, I created a "mapping array", which is an NSMutableArray that contains "pointers" to your actual data array. The map contains integers wrapped in NSNumbers. In its virgin state the map's index 0 has the integer 0 (wrapped in NSNumber), index 1 has integer 1, etc.
The UITableView delegate methods are built so that the map's index count is used for numberOfRowsInSection. In the cellForRowAtIndexPath method, it looks at the appropriate index of the map array, retrieves whatever is wrapped in the NSNumber, and then looks in that index of your actual data array.
The benefit of this dereference is that it becomes extremely easy to add and remove cells from the table. Just add/remove the NSNumber objects from your mapping array. Make sense? Sorry, not at my Mac or I could just put up some code samples.
Oh, and don't forget that you have to call the update method (the exact name escapes me) on your TableView so that it refreshes and the cells hide/unhide.
Had the same problem, and as I wanted to avoid some mapping as mentioned, I just set the cell-size to 0:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger row=[indexPath row];
float ret=0.0;
if( row==3) {
ret=0.0;
}
else {
ret=40.0;
}
return ret;
}
I used Matt's technique to create a mapping to cell data. Here is some code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return computeNumberOfRowsAndMapCellData;
}
// Compute mapToCellData to map the index of the cell to the cell data for the
// cell based on TaskConfig show/hide.
// Return the number of rows in section 1.
- (NSInteger)computeNumberOfRowsAndMapCellData {
if (mapToCellData) {
[mapToCellData release];
}
mapToCellData =[[NSMutableArray alloc] init];
if (cellData) {
[cellData release];
}
cellData = [[NSMutableArray alloc] init];
NSInteger numberOfRows = 12; // maximum number of rows
NSNumber *index = [NSNumber numberWithInt:0];
// If the data is not configured to show, decrement the number of rows in the table.
if ( ! [configManager isShowDateForType:task.case_type severity:task.severity]) {
numberOfRows--;
} else {
// Add a map to the cell data with the row number.
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[df stringFromDate:task.respond_due_date], #"Respond Due", nil];
[cellData addObject:dict];
[mapToCellData addObject:index];
int value = [index intValue];
index = [NSNumber numberWithInt:value + 1];
}
// Check the configuration for the rest of the rows of cell data.
....
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.numberOfLines = 0;
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.font = [UIFont fontWithName:#"Helvetica-Bold" size:15];
cell.detailTextLabel.font = [UIFont fontWithName:#"Helvetica-Bold" size:14];
}
NSUInteger mapIndex = 0;
// Use the mapToCellData to find cell data based on show/hide in ConfigManager for the data type.
mapIndex = [indexPath row];
NSNumber *cellDataIndex = [mapToCellData objectAtIndex:mapIndex];
NSDictionary *cellDataDict = [cellData objectAtIndex:[cellDataIndex unsignedIntegerValue]];
cell.textLabel.text = = [[cellDataDict allValues] objectAtIndex:0];
cell.detailTextLabel.text = [[cellDataDict allKeys] objectAtIndex:0];