how to define uitableviewcells - iphone

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.

Related

Don't display empty grouped cell tableview

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.

UITableView Reuse Causing Broken Cells

When scrolling my UITableView (tends to be when I scroll it fast) the data for my cells gets mixed up, so labels might be repeated etc.
I understand that reusing the cells probably causes this, but what if the user scrolls down the list really quickly and all the cells get mixed up, how am I supposed to avoid this?
Thanks.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"VideoListCell";
VideoListCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[VideoListCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
if (isPlaylistView)
{
//Fill cell with data
}
else if (isPlaylistDetailView || isSearchResultsView)
{
//Fill cell with data
}
else
{
//Playlist button and uploads
if (indexPath.section == 0)
{
//Fill cell with data
}
else
{
//Fill cell with data
}
}
return cell;
}
You generally use this kind of code:
cell = dequeReusableCell;
if (cell == nil) {
create cell;
initialize cell;
}
fill cell with actual data from current row
return cell;
If you will move code "fill cell with actual data from current row" into "if" — you will get the kind of behavior you get right now.
So the answer will be "fill cell with data after you initialize it, outside of "if (cell == nil)" block.
UITableView will ever only dequeue a cell for reuse if the position that the cell was in is currently off-screen. So you don't have to worry about "mix-ups".
static NSString *cellIdentifier=#"cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell==nil)
{
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
}
I think it will be helpful to you.
set dequeueReusableCellWithIdentifier to nil for example..
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
UPDATE:
See this Example... i load many data in the cell with also my custom Gridview...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* cellIdentifier = #"gridCell";
UITableViewCell *gridCell = [tableView dequeueReusableCellWithIdentifier:nil];
if(gridCell == nil)
{
gridCell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
return gridCell;
}
hope this help you....
In your custom cell class override prepareForReuse method. In this method set text of your labels to nil and set imageview's image to nil also. This function is called everytime when a cell is reused so your problem will be solved by this. May be like this
- (void)prepareForReuse{
[super prepareForReuse];
self.titleLabel.text = nil;
self.unitImageView.image = nil;
}

How to reload a single section in a UITableView

Hey I'm having trouble trying to figure this one out. UITableView has a method [tableView reloadSections:(NSIndexSet *) withRowAnimation:(UITableViewRowAnimation)] now according to the Apple docs this method takes in an NSIndexSet object and reloads what ever section specified by the index set. The problem is even though im only sending in one section to this method it ends up reloading all the sections and i can't understand why this is happening. Any help will be greatly appreciated
Even if you try reloading only a section, you must also provide the populating process section by section:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"DetailViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
if (indexPath.section == 0){
//blablabla
cell.textLabel = blabla
}
else if (indexPath.section == 1){
///blablabla
cell.textLabel = blabla2
}
return cell;
}
... because reloading the table (is it only a section or the whole table) wil call the method above

UITableview mixes up cells

i have a problem with my UITableview. After scrolling or pushing the switch it starts sometimes mixing up the cells. So you can so the text of one cell shows up in the other. I read a lot about tableViews, but I found nothing that fits for my code.
Here it is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
}
if (indexPath.section == 0) {
if (indexPath.row == 0) {
cell.textLabel.text = NSLocalizedString(#"Course:",#"Course Section");
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.detailTextLabel.text = entryTableCourseName;
}
else if (indexPath.row == 1) {
cell.textLabel.text = #"Due Date:";
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.detailTextLabel.text = entryTableDueDateString; }
}
else if (indexPath.section == 1) {
if (indexPath.row == 0) {
//cell.textLabel.text = NSLocalizedString(#"Title", #"entryTableTaskTitle");
[cell.contentView addSubview:nameField];
}
else if (indexPath.row == 1) {
[cell.contentView addSubview:TaskView];
}
}
else if (indexPath.section == 2) {
if (entryTableGradeSwitch.on) {
if (indexPath.row == 0) {
cell.textLabel.text = NSLocalizedString(#"Grading", #"GradingCell");
cell.accessoryView = entryTableGradeSwitch;
}
else if (indexPath.row == 1) {
cell.textLabel.text = NSLocalizedString(#"Options", #"GradingCellOptions");
}
}
else {
cell.textLabel.text = NSLocalizedString(#"Grading", #"GradingCell");
cell.accessoryView = entryTableGradeSwitch;
}
}
}
I feel like I'm doing something really wrong on setting up the cells.
It's all due to cell-reusability. There are plenty of posts about this on StackOverflow, and I would recommend that you search for "UITableViewCell Reusability Problems", and you will come across the solution.
In short .. What happens here is that when you are scrolling, the cell essentially re-uses the one used before at that indexPath, and cellForRowAtIndexPath assigns a different cell at that indexpath.
Solution ?
Use different reuse identifiers for each cell.
Subclass your tableViewCell with the view or label that is getting messed up.
Make sure you implement
- (void) prepareForReuse
in your cell if you reuse them. In this routine clean up anything you added to the cell that you don't want to show up in another reuse. If you don't add any custom items just make sure when you populate a cell that you set everything even if it's empty.
Every time you do this:
[cell.contentView addSubview:TaskView]
Or something like it, you are adding a new subview to the cell. As the cells get reused, you add more and more subviews.
From your code, it isn't even clear where nameView and taskView come from. They should be either added once when a cell is first created, and just configured thereafter, or they should be part of your cell as designed in the xib or storyboard.
Here's a super-simple fix for this, and you still get to keep your reuse-identifiers! Also no subclassing or anything needed.
It's simple: just make sure and set all of your labels and views. This means that if one cell isn't going to use the detail text label, still set it to #"". By doing this, you make sure that no leftover data from the content that used the cell before is still there.
Super simple, and works every time! Also way better resource wise than not reusing your cells.

UITableView Checklist Problem

I have this bizarre problem. I'm making a checklist program with XCode and I'm using UITableView with UITableViewCellAccessoryCheckmark. I can select cells and the checkmark will appear, but somehow, other cells that I have NOT yet selected below will also have a checkmark appear. Any ideas?
Here's my check mark coding:
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;
cell = [aTableView cellForRowAtIndexPath: indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
I don't know if this affects it, but I also implemented this method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
// Dequeue or create a cell of the appropriate type.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryNone;
}
// Configure the cell.
if(tableView == Table1){
switch(indexPath.section){
case 0:
cell.textLabel.text = [array1 objectAtIndex:indexPath.row];
break;
case 1:
cell.textLabel.text = [array2 objectAtIndex:indexPath.row];
break;
case 2:
cell.textLabel.text = [array3 objectAtIndex:indexPath.row];
break;
}
//cell.textLabel.text = [NSString stringWithFormat:#"Row %d", indexPath.row];
}
if(tableView == Table2){
cell.textLabel.text = [array4 objectAtIndex:indexPath.row];
}
return cell;
}
Thanks.
Set the cell.accessoryType each time you call tableView:cellForRowAtIndexPath:. Otherwise, when you reuse a cell, you'll get it's accessoryView instead of what you're expecting.
So, yeah, you'll need to keep track in when NSIndexPaths are selected by some method other than just looking at the accessoryType.
You should keep the checked/unchecked info in data source (array).
I also advise you to remove the cell.accessoryType = UITableViewCellAccessoryNone; line.
This line will be executed only for few first cells. All the other cells will be "reused" - meaning that the old (already initiated) cells will be used, and you will have to modify the details that are displayed on these cells (all inside cellForRowAtIndexPath as you do now).
In addition you will have to add a similar line (something like cell.accessoryType = ([[array objectAtIndex:indexPath.row] boolValue] ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;).
In general, I suggest you to use one array for holding the data for your table, when each item in the array will be a dictionary. This way you will be able to hold the texts and the boolean checkmarks (inside NSNumber) and easily access them when needed.