How to know if cell is first in section - iphone

When customizing an UITableView, how can I know wether the cell is the first, last or only one in the section in the
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath`
method?
Thanks.

To find out if it is the last row in a section, call tableView:numberOfRowsInSection: on the data source delegate (presumably, it's self, since you are in a tableView:cellForRowAtIndexPath: method). If the value returned from that method equals indexPath.row+1, you are at the last row. If additionally indexPath.row == 0, you are at the only row. If indexPath.row == 0 but tableView:numberOfRowsInSection: returned a number other than 1, you are looking at the first row in a section.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger count = [self tableView:tableView numberOfRowsInSection:indexPath.section];
BOOL first = indexPath.row == 0;
BOOL last = indexPath.row+1 == count;
if (first && last) {
NSLog(#"The only cell in section %d", indexPath.section);
} else if (first) {
NSLog(#"The first cell in section %d", indexPath.section);
} else if (last) {
NSLog(#"The last cell in section %d", indexPath.section);
} else {
NSLog(#"Nothing special...");
}
// Do the rest of your work
}

NSIndexPath has all the information you need. Row and section.

Related

collapsing a UITableView

Hey everyone I made a UITableView in my app and when a cell is touched it expands the problem I am having is it does not collapse no matter what I have tried, I'm sure its something easy i just cant figure it out the only time it does collapse is when it another cell is tapped.
Here is my code:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
selectedCellIndexPath = indexPath;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
if (selectedCellIndexPath) {
selected = YES;
}
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(selectedCellIndexPath != nil
&& [selectedCellIndexPath compare:indexPath] == NSOrderedSame)
return 150;
return 44;
}
You're never changing selectedCellIndexPath to nil, so the current selected row doesn't get changed until a new one is selected. In didSelectRowAtIndexPath you should change the beginning to the following:
if (selectedCellIndexPath == indexPath)
selectedCellIndexPath = nil;
else
selectedCellIndexPath = indexPath;
In selectedCellIndexPath you are storing pointer of indexPath. It can change when you reload table, means indexPath object of same cell can be different when you select it second time.
It's safer if you store indexPath.section & indexPath.row
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(_section == indexPath.section && _row == indexPath.row)
{
_section = -1;
_row = -1
}
else
{
_section = indexPath.section;
_row = indexPath.row;
}
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
if (selectedCellIndexPath) {//change it as per your requirement
selected = YES;
}
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(_section == indexPath.section && _row == indexPath.row)
return 150;
return 44;
}
I have created the collapsing UITableView
https://github.com/floriankrueger/iOS-Examples--UITableView-Combo-Box/zipball/master
http://www.codeproject.com/Articles/240435/Reusable-collapsable-table-view-for-iOS
https://developer.apple.com/library/ios/#samplecode/TableViewUpdates/Introduction/Intro.html
it's working great..
When you call row reload function does it enter the cellForRow delegate function? If it does then you should put some functionality to collapse the rows after checking for selected row.

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.

UITableView action row

On the WWDC 11 video 'session 125 - UITableCiew Changes, Tips, Tricks' at 24:30+ Mr Luke Hiesterman is giving a demo which adds a cell to the table view when a cell is selected.
I want to add that functionality to my IOS application, but I can't figure out how to make it.
Some code is not shown in the demo-video. And there is no downloadable source of the demo.
Can anyone help me out?
EDIT:
I can add a new row below the selected row but it is an other custom cell.
(I have a list of contracts which you can accept)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return ((indexPath.row >= [_contracts count]) ? 40 : 60);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_contracts count] + ((_addedRow) ? 1 : 0);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *reuseID = #"contract_cell";
ContractTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseID];
if(!cell)
{
// Load the top-level objects from the custom cell XIB.
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"ContractTableViewCell" owner:self options:nil];
// Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
cell = [topLevelObjects objectAtIndex:0];
[cell setBackgroundView:[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"contract_cell.png"]]];
}
NSLog(#"row?: %d", indexPath.row);
//I thought this would work... not.
if((indexPath.row >= [_contracts count]))
{
[cell.label setText:#"new"];
}
else
{
Contract *contract = [_contracts objectAtIndex:indexPath.row];
[cell.label setText:#"test"];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(!_addedRow)
{
_addedRow = YES;
[tableView deselectRowAtIndexPath:indexPath animated:NO];
[_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section]] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
return [NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section];
}
For example I press the first row. A row is added below the first one. But now the cellForRowAtIndexPath.. is called for row 2 (needs to be the new custom)
how can I check if it is a new one?
You need to remember which of your rows is selected to show the added row at the correct index. Currently you're always assuming that the added row is the very last one because you're configuring it in tableView:cellForRowAtIndexPath: only if the index is bigger than your number of contracts.
Say you have five contracts and the third one is selected. if((indexPath.row >= [_contracts count])) is only true for the last row but actually want this condition to be true for the fourth row, so it should be if (indexPath.row == selectedRowIndex + 1) (you need to store the selected row index in some instance variable).
This answer should help you out. It tells you how to put your UITableViewController into "update mode" and then lets you insert new rows (even with animation): UITextField in UITableViewCell - adding new cells

UITableView Putting array objects into sections

Hey guys I need to know who to put these array objects into two separate sections, that means one section for the red cell and another section for the blue cell. Would really appreciate your help been stuck on this all day now. Here is my code:
-(void)viewDidLoad {
[super viewDidLoad];
//Initialize the array.
array = [[NSMutableArray alloc] init];
[array addObject:#"Red"];
[array addObject:#"Blue"];
self.ViewTable.backgroundColor = [UIColor clearColor];
}
You need to implement:
return number of sections:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
return number of rows for the requested section
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
return correct cell by reading both indexPath.row and indexPath.section
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
So for red you would look for a cell request that has indexPath.section equal to 0 and indexPath.row equal to 0. blue would be indexPath.section equal to 1 and indexPath.row equal to 0
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 2; // This returns 2 sections
}
Updated:
In the cellForRowAtIndexPath
NSInteger section = [IndexPath section];
if (section == 0)
// write your code for red here
if (section == 1)
// write your code for blue here

Customize a data-driven TableView

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.