Don't display empty grouped cell tableview - iphone

I'm doing a similar view to View Contact in Contacts.app, meaning I have multiple fields that are not required (second work phone, second email, etc.) under different sections (Phones, Emails, etc.).
When some fields are empty, I don't want to display them, when all the fields are empty under a section, I don't want to display this section. Also, some of the cells have action on them, like when tapping on a phone number cell, it calls the number displayed. Currently, these actions are manually handled in didSelectRowAtIndexPath with basic ifs depending of the cell position.
I can't find an elegant solution to do all this... I tried an array of dictionaries (each cell) for each section but things rapidly went messy. Also, since the order of the rows is never the same, I can't easily with ifs handle all the actions in the didSelectRowAtIndexPath method based on the cell position.
Oh and I'm using Core Data under the hood.
Anybody had to do something similar and is willing to share his thoughts?
Thanks!
Exemple of my static ifs setup right now to differentiate cells:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (indexPath.section == 0)
{
// Custom actions here
}
else if (indexPath.section == 1)
{
// Other actions here
[self showMailComposeWithEmail:cell.textLabel.text];
}
}
And another method using indexPath to differentiate styles and behaviors:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if (indexPath.section == 0)
{
if (indexPath.row == 0)
{
cell.textLabel.text = self.contact.phone;
}
}
else if (indexPath.section == 1)
{
if (indexPath.row == 0)
{
cell.textLabel.text = self.contact.email;
}
}
return cell;
}

Take a dictionary and when you add object to that dictionary make sure that the data in it is not blank. Add it data in dictionary for keys as below:
Phones - key 0^0 (it means at 0 section 0 row)
WorkPhone - key 0^1(it means at 0 section 1st row)
Emails - key1^0 (1 section 0th row) and so on...
And at cellForRowAtIndexPath fetch this value as
[dict valueForKey:[NSString stringWithFormat:#"%d^%d",indexPath.section,indexPath.row]];
Hope thats clear.

Related

UITableView moves cell contents around when scrolling

I have seen other questions on here related to this, but I have their solutions in place, and things still move around when I scroll.
Basically, I have a table of data and I want the first and only the first row to have a UITableViewCellAccessoryDisclosureIndicator. The problem is, when I scroll around, the indicator duplicates, deletes, and moves to other cells. Here is my code..
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if(indexPath.row == 0) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.textLabel.text = [tabledata objectAtIndex:indexPath.row];
return cell;
}
The problem is something with the indexPath.row variable.. its somehow returning 0 in other cells that aren't the very first (when it comes to displaying cells). HOWEVER... the data is always right from my array(which implies the value HAS to be correct) and in my
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
method, i only wish to change screens when row 0 is clicked.. and regardless of where I have scrolled to, no cells trigger except the very first. So it seems THAT check is always correct while the one inside cellForRowAtIndexPath is not...
Most likely, you are re-using cells that already have the accessoryType set. You need to explicitly set it to NOT show on cells where it shouldn't.
if(indexPath.row == 0){
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}

Add Blank Cell to each Group in UITableView

I have a tableview that has groups. I would like to add a dummy cell at the top of each group, but not add it to the data. I dont want it saved. So if the top cell is clicked, its data passed will be blank and I can handle the passed view differently. I currently am configuring the cells manually.
So if group one had two entries, the display of those two entries would be prepended with a fake entry at what would be index 0
Below is a preview of what I am trying to do.
PS: Can one cell have a different disclosure icon? Does that break Interface Guidelines?
It would be something like this, you are not really adding anything to your model, you are just modifying your view:
- (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];
}
if ([indexPath row] == 0) {
//And any other default behavior
[[cell textLabel] setText:#""];
}
else{
//Your code for each cell
}
return cell;
}
The HIG doesn't mention anything about having different disclosure icons:
iOS includes some table-view elements that can extend the functionality of table views. Unless noted otherwise, these elements are suitable for use with table views only.

how to define uitableviewcells

I am creating a uitableview that has two sections, section == 0 has 5 rows, and section == 1 has 1 row.
I have also declared several functions in an objective-c class that I would like to hook into each of the five rows. However I am not sure how to implement this.
I am thinking it is something like
- (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];
}
// Configure the cell...
cell.selectionStyle = UITableViewCellSelectionStyleNone;
//---------- In here put a bunch of IF statments, declaring each cell? and linking it to
//---------- the function I have declared in the objective-c class
return cell;
}
Usually the cells are setup in tableView:cellForRowAtIndexPath:. Determining the function that's called when a cell is tapped is usually done in tableView:didSelectRowAtIndexPath.
When setting up the cell in tableView:cellForRowAtIndexPath: you can specify the section and row using indexPath like this:
if(indexPath.section == 0){
if(indexPath.row == 0){
// setup this cell...
}else if(indexPath.row == 1){
}// ...
}else if(indexPath.section == 1){
if(indexPath.row == 0){
}else if(indexPath.row == 1){
}// ...
}
Do something similar in tableView:didSelectRowAtIndexPath when determining which function is called when the user taps each cell. The relevant documentation from Apple can be found here.
Typically this function would be configuring the cell by pulling data out of an array, database, or some other indexed collection:
if ([indexPath section] == 0) {
[[cell textLabel] setText:[sectionOneValues objectAtIndex:[indexPath row]]];
}
else if ([indexPath section] == 0) {
[[cell textLabel] setText:[sectionTwoValues objectAtIndex:[indexPath row]]];
}
In your case it sounds like you want to "hook into" some other functions for the 5 rows in section 0, so you'd be doing something other than using a sectionOnValues array in the if clause. It's a little hard to be more specific without knowing what you mean to do with these functions that hook in.
Depends, are all the cells equal, but only changing their content? In that case, you would be right.
On the other hand, if you plan on adding more Labels, images, or whatnot into the cell, then, for each different row, you would have to tweak the way the cells are being created, therefore, having to add new CellIdentifiers to avoid mixing cells, and changing the code inside the if (cell == nil) {} to configure each cell design.

Use two different cell identifier on same table view

I'm trying to get rid of a cached UITableView cell. I have two sections. The first cell of the second section has the same "appearance" as the first cell of the first section. With appearance I mean the cell height (multiline cells).
I tried to use different identifiers, but that didn't worked. Here is the code:
NSString *identifier;
if (thisViewMode) {
identifier = #"thisViewMode";
} else if ((indexPath.section == 1) && thatViewMode) {
identifier = #"thatViewMode";
} else {
identifier = #"CellIdentifier";
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [self CreateMultilinesCell:identifier];
}
thatViewMode is only called once, which isn't right because I have more cells in my second section. If I change the content of the first cell in the first section, the height of the first cell in the second section also changes too. Normally each cell should have it's own cell height, but that isn't the case here.
Is there a problem that I can't use different cell identifiers on the same table view?
I forgot to also adapt heightForRowAtIndexPath so that I make a differentiation between the sections.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 1) {
// do something
} else {
// do something different
}
return ...
}

UITableViewCell: how to verify the kind of accessoryType in all cells?

I have a UITableView in that some cells are marked with UITableViewCellAccessoryCheckmark at the initialization of the view.
When the user selects another row, I have to check if the maximum number of selected rows was achieved before. To do that, I used the code bellow:
- (NSInteger)tableView:(UITableView *)tableView numberOfSelectedRowsInSection:(NSInteger)section{
NSInteger numberOfRows = [self tableView:tableView numberOfRowsInSection:section];
NSInteger numberOfSelectedRows = 0;
for (int i = 0; i < numberOfRows; i++) {
UITableViewCell *otherCell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:section]];
if (otherCell.accessoryType == UITableViewCellAccessoryCheckmark) {
numberOfSelectedRows++;
}
}
return numberOfSelectedRows;
}
If my number of rows is, as example, 20, the variable numberOfRows is setted correctly with 20. Lets say that 13 rows already are marked with UITableViewCellAccessoryCheckmark. So, numberOfSelectedRows should be 13 after the loop, but only the marked and VISIBLE cells are considered. So, if I have 9 cells showed and 7 are marked, the numberOfSelectedRows returns 7 instead of 13 (but the for iterate 20 times, as expected).
Is this a correct behavior of UITableView or it is a bug of iPhone simulator?
Thanks in advance.
Yes, it works as designed. You should never store model data in your views. UITableView knows nothing about the data, it only displays cells (and throws them aways as soon as they scroll off the screen). You need to store the checkmark state of each cell in a model object (e.g. an array) that you then access from your view controller.
This is correct behavior.
The UITableView is not a list. The system caches cell that are off screen to save memory and CPU and they can not be iterated over in a manner that makes sense.
Ok, you should keep track of the model/data and the tableView will keep track of displaying it. I have had some problems with this until I accepted that uitableView is not a list:)
So, have an array of objects that each corresponds to the data in the a cell. When building the individual cells like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"categoryCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
Item *item = [self.itemList objectAtIndex:indexPath.row];
[cell.textLabel setText:[item itemBrand]]; //notice that here we set the cell values
return cell;
}
The when a user clicks you change you model like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"IndexPat.row%i", indexPath.row);
Item item = (Item*) [self.itemList objectAtIndex:indexPath.row];
//change the state of item
}
This way the tableView will update to resemble the model/data, you just managed the model.