Reusing UITableView Cells causes crash when a cell is "Recycled" - iphone

I am trying to re-use cellViews using tags and Cell Identifiers, however the code below crashes whenever a cell is re-used. I think I'm almost there. Can anyone see the mistake?
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
const NSInteger BUTTON_TAG = 1001;
const NSInteger SWITCH_TAG = 1002;
const NSInteger TEXTFIELD_TAG = 1003;
NSString *CellIdentifier = #"";
if(indexPath.section == 2 && indexPath.row == 0)
CellIdentifier = #"Button";
else if (indexPath.section == 3)
CellIdentifier = #"Switch";
else
CellIdentifier = #"TextField";
UISwitch *switchView;
UITextField *textField;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
if (CellIdentifier == #"TextField")
{
cell.selectionStyle = UITableViewCellSelectionStyleNone;
CGRect frame = CGRectInset([cell.contentView bounds], 70, 10);
textField = [[[UITextField alloc] initWithFrame:frame] autorelease];
textField.keyboardType = UIKeyboardTypeDefault;
textField.returnKeyType = UIReturnKeyDone;
textField.autocorrectionType = UITextAutocorrectionTypeNo;
textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
textField.delegate = self;
cell.accessoryView = textField;
cell.tag = TEXTFIELD_TAG;
}
else if (CellIdentifier == #"Button")
{
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
cell.textLabel.textAlignment = UITextAlignmentCenter;
cell.clipsToBounds=YES;
cell.tag = BUTTON_TAG;
}
else if (CellIdentifier == #"Switch")
{
cell.selectionStyle = UITableViewCellSelectionStyleNone;
switchView = [[[UISwitch alloc] initWithFrame:CGRectZero] autorelease];
cell.accessoryView = switchView;
cell.tag = SWITCH_TAG;
}
}
else
{
textField = (UITextField*)[cell viewWithTag:TEXTFIELD_TAG];
switchView = (UISwitch*)[cell viewWithTag:SWITCH_TAG];
}
Crash Log
2012-02-22 14:50:08.352 ***[2304:207] -[UITableViewCell setSecureTextEntry:]: unrecognized selector sent to instance 0x6368270
2012-02-22 14:50:08.355 ***[2304:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITableViewCell setSecureTextEntry:]: unrecognized selector sent to instance 0x6368270'

You don't say what the crash is or provide a backtrace, but one problem I see right away is that you are always doing:
switchView = (UISwitch*)[cell viewWithTag:SWITCH_TAG];
for all recycled cells even though only one of the three types has a switchView.
You also only set TEXTFIELD_TAG for one kind of cell as well, yet refer to it when accessing all types of "recycled" cells.
EDITED TO ADD: I see you've added the exception from your console. The exception is being thrown on a call to setSecureTextEntry. I don't see setSecureTextEntry anywhere in the code you copy & pasted into the question, so I'd suggest looking for setSecureTextEntry in your real code and wherever it's being called, make sure it's a UITextField receiving that call and not a UITableViewCell (which can be the super view in which a secure UITextField lives).

Beside the commented cell alloc line you are setting the .tag properties to cells instead of text fields and switches, this is most probably the cause of crashes. Also post a crash log, so we'd see what exactly crashes the app.

Start by adding you new controls to the contentView (or cell) as subviews, like this:
[cell.contentView addSubview:textField];
...and so on for the other views. See if that fixes the crash.
Also, as #Eugene points out, it's not going to help that you set the tag on the cell. Set the tag on the views you create. (Though I doubt that's behind your crash).

Related

When we scroll custom table then cell content change in iPhone

hello friends I have created custom table and add all needed delegate and datasource for table then this is working good but when we scroll my table then content change like this is code...
Table_worklist=[[UITableView alloc]initWithFrame:CGRectMake(10,480,750,130)style:UITableViewStylePlain];
Table_worklist.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
Table_worklist.delegate=self;
Table_worklist.dataSource=self;
Table_worklist.layer.borderWidth = 2.0;
Table_worklist.layer.borderColor = [UIColor grayColor].CGColor;
[ScrollView addSubview:Table_worklist];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell== nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] ;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UITextField *resultval=[[UITextField alloc]initWithFrame:CGRectMake(510,10,120,30)];
resultval.tag=1;
resultval.font = [UIFont boldSystemFontOfSize:12.0];
//the horizontal alignment of the text
resultval.textAlignment = NSTextAlignmentCenter;
resultval.contentVerticalAlignment = UIControlContentHorizontalAlignmentCenter;
resultval.clearButtonMode = UITextFieldViewModeWhileEditing; // has a clear 'x' button to the right
resultval.delegate =self;
if([[NSString stringWithFormat:#"%#",[[TestSample objectAtIndex:indexPath.row]valueForKey:#"ResultType"]]isEqualToString:#"Numerical"])
{
imageLayer = field.layer;
[imageLayer setCornerRadius:02];
[imageLayer setBorderWidth:1];
imageLayer.borderColor=[[UIColor lightGrayColor]CGColor];
}
else if([[NSString stringWithFormat:#"%#",[[TestSample objectAtIndex:indexPath.row]valueForKey:#"ResultType"]]isEqualToString:#"Words"]||[[NSString stringWithFormat:#"%#",[[TestSample objectAtIndex:indexPath.row]valueForKey:#"ResultType"]]isEqualToString:#"Comment"])
{
imageLayer = field.layer;
[imageLayer setCornerRadius:06];
[imageLayer setBorderWidth:1];
imageLayer.borderColor=[[UIColor blackColor]CGColor];
UIButton *CheckMark=[[UIButton alloc]initWithFrame:CGRectMake(540,10,30,30)];
CheckMark.tag=1;
[CheckMark setImage:[UIImage imageNamed:#"DownTriangle1"]forState:UIControlStateNormal];
imageLayer = CheckMark.layer;
[imageLayer setCornerRadius:02];
[imageLayer setBorderWidth:1];
imageLayer.borderColor=[[UIColor blackColor] CGColor];
[cell.contentView addSubview:CheckMark];
}
[cell.contentView addSubview:resultval];
}
return cell;
}
this show right data for first 3or4 cell(because table height 130 so show only 3 cell at a time )for these 3 cell my table show right data ...for example
if([[NSString stringWithFormat:#"%#",[[TestSample objectAtIndex:indexPath.row]valueForKey:#"ResultType"]]isEqualToString:#"Numerical"])
{ }
then data is numerical go this if condition other wise go else condition......
but when we have for more cell like as 7-8 cell then when we scroll these cell so what happened suppose at cell-4 my controller go else if condition forComment and word data .........so at last cell -8 got numerical data so controller go in if condition but check mark button which was created in if else condition which show on my if condition data ......so i don't know what happened
when we scroll how to change my condition ......
Actually i thing when last created else if condition (which create uibutton checkmark)on cell-4 which are apply on cell-8...........but controller go in right place in if condition at cell-8 .....but last time created checkmark button generated on cell-8 ......
how to solve this problem....
Before You follow my Answer i want to tell you that following code is bad for memory management because it will create new cell for each rows of UITableView, so be careful for it.
But it is better to use, When UITableView Have Limited rows (about 50-100 may be ) then following code is helpful in your case, use it if is it suitable for you.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [NSString stringWithFormat:#"S%1dR%1d",indexPath.section,indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
/// Put your code here
}
return cell;
}
If you have limited rows then this is best code for you.

Inserting UITextField into a TableView

I wanted to have textFields in a tableView. So I inserted them manually in the tableView in the storyboard and modified their individual properties. However, I could not see the text fields when I ran the program. I repeated the same procedure with a switch. But it still did not run. So I figured that the objects are not being shown despite the fact that they are present in the storyboard.
Q1. Any idea why this is the case?
In order to overcome this problem, I inserted these objects programmatically. For UISwitch, I added the following code in cellForRowAtindexPath right after initWithStyle:style reuseIdentifier:reuseIdentifier.
UISwitch *newSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(25, 55, 77, 27)];
[newSwitch addTarget: self action: #selector(pictureSwitchAction:) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:newSwitch];
This works fine. pictureSwitchAction method is called as expected.
However, when I try to do similar thing with UITextField, the app crashes. (FYI... field0 is a declared as property.) Here's my code:
field0 = [[UITextField alloc] initWithFrame:CGRectMake(0, 50, 320, 44)]; // (1)
cell = (UITableViewCell *)[field0 superview]; // (2)
[self.view addSubview:field0]; // (3)
(1) and (2) (commented (3)) crash the app. Error: *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
My understanding is that the cell has to be return first before I can allocate some value to it. But (1) and (3) (commented (2)), yield no result. I don't know why this works for a switch and not UITextField.
Any responses to Q1 and how to insert textField programmatically?
Thanks!
Edit 1
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone; // VERY IMPORTANT
if(indexPath.section == 2)
{
if (indexPath.row == 0)
{
field0 = [[UITextField alloc] initWithFrame:CGRectMake(0, 50, 320, 44)];
cell = (UITableViewCell *)[field0 superview];
// [self.view addSubview:field0];
}
if (indexPath.row == 1)
{
field1 = [[UITextField alloc] initWithFrame:CGRectMake(0, 50, 300, 43)];
cell = (UITableViewCell *)[field1 superview];
// [self.view addSubview:field1];
}
if (indexPath.row == 2)
{
field2 = [[UITextField alloc] initWithFrame:CGRectMake(0, 50, 300, 43)];
cell = (UITableViewCell *)[field2 superview];
// [self.view addSubview:field2];
}
}
return cell;
}
Here You should make the Change in Your Code like As
Answer-1:- If you want to customize cells by simply adding additional views, you should add them to the content view .so they will be positioned appropriately as the cell transitions into and out of editing mode.
Answer-2 Crash Reason:- Because you were not returning UITableViewCell.As Crash Log ItSelf Explain The same *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
Answer 3 -For this My understanding is that the cell has to be return first before I can allocate some value to it.Here For this Line i would say you are totally wrong simply think. can you put mango inside the basket even if you don't have that basket.means you will need that . so the same concept is here whenever you are going to add something like textfield the TableCell you need to create(allocate) that first and then you can add any object over that tableCell and finally return that tableCellAs You would see inside the CellForRowAtIndexPath method.
I think you clear now.
For The UISwitch you were adding it over the self.view means controller's view.see your code [self.view addSubview:] showing that switch over the mainView(conrtoller' view) rather Shwoing over the tableViewCell .and for the TextField it was not working because of this line cell = (UITableViewCell *)[field0 superview];it's totally wrong.
Just for your kind Information see below code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone; // VERY IMPORTANT
if(indexPath.section == 2)
{
if (indexPath.row == 0)
{
UITextField* thisTextField =[[UITextField alloc] initWithFrame:CGRectMake(originX, originY, width ,hieght)];//pass the co-ordinate.
[thisTextField setBackgroundColor:[UIColor clearColor]];
[thisTextField setTag:indexPath.row];//here by setting this you can access further it
thisTextField.delegate= self;
[[cell contentView] addSubview:thisTextField];
// If you want to customize cells by simply adding additional views,
// you should add them to the content view .
// so they will be positioned appropriately as the cell transitions into and out of editing mode.
}
return cell;
}
I would suggest you should start from the basic of view Hierarchy and UITableView.
Thanks

What is wrong with my UITableView cellForRowAtIndex for Single Selection?

Below is code for UITableView, But when i scroll its behaves weirdly (too annoying)... This problem is due to reuseIdentifier.... but dont know how to solve..
- (UITableViewCell *)tableView:(UITableView *)tableView1 cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView1 dequeueReusableCellWithIdentifier:CellIdentifier];
NSInteger imgTag = 1;
NSInteger lblTag = 2;
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(2, 2, 52, 52)];
// Image:[UIImage imageNamed:[self.glassType objectAtIndex:indexPath.row]]];
imgView.tag = imgTag;
[cell.contentView addSubview:imgView];
[imgView release];
UILabel *lblName = [[UILabel alloc] initWithFrame:CGRectMake(60, cell.frame.size.height/4, 200, 21)];
// lblName.text = [self.glassName objectAtIndex:indexPath.row];
lblName.tag = lblTag;
[cell addSubview:lblName];
[lblName release];
}
NSInteger imgIndex = 2;
NSInteger lblIndex = 3;
((UIImageView *)[cell viewWithTag:imgTag]).image = [[self.glassType objectAtIndex:indexPath.row] objectAtIndex:imgIndex];
((UILabel *)[cell viewWithTag:lblTag]).text = [[self.glassName objectAtIndex:indexPath.row] objectAtIndex:lblIndex];
return cell;
}
- (void)tableView:(UITableView *)tableView1 didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView1 cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
How to make Cell for row at index so that it remains constant even when scrolled??? Also how to make single selection in UITableView??
The answer is that you should not add subviews to your table cells outside of the "if (cell == nil) { ..." clause or they get added over and over again to the same cell when it gets re-used.
See my answer to this question for a more detailed explanation, including code for how to fix it:
cellForRowAtIndexPath memory management
You also cannot store state in table cells because as soon as they scroll offscreen they are recycled and re-appear at a different index in your table. You need to set up an array of model objects to store state for your table cells (such as what their accessory type should be). A more detailed explanation can be found in my answer to this question:
Looping through UITableViewCells of a UITableView
If you fix how you are adding subviews to the cells, and store your "ticked" state in an array of model objects as well as setting the cell.accessoryType (so that it can be restored when the cell is dequeued), then your approach to row selection is otherwise correct.
So put this in your tableView:cellForRowAtIndexPath: method, just before the return cell;:
MyModelObject *object = [self.arrayOfModelObjects objectAtIndex:indexPath.row];
BOOL isChecked = object.checked;
cell.accessoryType = isChecked? UITableViewCellAccessoryCheckmark: UITableViewCellAccessoryNone;
And in your tableView: didSelectRowAtIndexPath: method, get rid of the current logic and replace it with:
- (void)tableView:(UITableView *)tableView1 didSelectRowAtIndexPath:(NSIndexPath *)indexPath
for (int i = 0; i < [self.arrayOfModelObjects count]; i++)
{
MyModelObject *object = [self.arrayOfModelObjects objectAtIndex:i];
object.checked = (i == indexPath.row); // only check the one we just tapped
}
//refresh table to update the accessory views for all rows
[tableView1 reloadData];
}
Obviously replace the arrayOfModelObjects with your own model implementation. You could just use an array of NSNumber objects containing bools if you don't want to create a custom class for this purpose.
The recycling queue is like a pool where previously created Cells are stored before to reuse them. For example when you scrolls up, at the moment the cell disappears above, it is stored in the queue and becomes available for the cell that will appear at the bottom. Ok ?
Actually the number of cells really created is exactly the max simultaneous cell you can display in your table (in most cases from 3 to 8). In other words your if (cell == nil) code is executed (more or less from 3 to 8 times) at the first reloadData to create the pool of cells your table needs.
Then all you make on a cell is kept as it and appears again when you dequeue it. It's now easy to understand that, in your code, you have to make all strictly row-dependant settings outside the if (cell == nil) block. The same way, do not add subViews outside the if (cell == nil) block, you can imagine the thousands of subview you will add each time you reset a dequeued cell !
Tip: if you need some custom cleanup before reusing a cell (like to set an image to blank), you can create a custom UITableviewCell class and implements the prepareForReuse method.
Is it clear ?
Always reload your tableView in viewWillAppear method instead of viewDidLoad.
(void) viewWillAppear:(BOOL)animated{
[self.tableView reloadData];
}
This avoids most of all unexpected and annoying problems. :)

UITableView Loaded from MutableArray turn in to a tickbox list

I have an tableview which is loaded from a mutablearray as listed below. However I need to asign each item in the array an ID and have a tickbox next to the item. Basically it's preferences for our search, it lets users prioritise by whichever tickboxes are ticked. So I'll want to save which items are ticked to a plist or similar.
Heres how the array is loaded:
arryTableIconsText = [[NSMutableArray alloc] init];
[arryTableIconsText addObject:#"Facilities for partially sighted or blind people"];
[arryTableIconsText addObject:#"An 'assistance dogs welcome' policy"];
[arryTableIconsText addObject:#"Disabled access facilities for wheelchair users (with assistance)"];
*more items added here*
arryTableIcons = [[NSMutableArray alloc] init];
[arryTableIcons addObject:#"visuallyImpaired_off.png"];
[arryTableIcons addObject:#"guidedogs_off.png"];
[arryTableIcons addObject:#"wheelchairassist_off.png"];
*more items added here*
And then loaded in to a table like so:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.numberOfLines = 0;
cell.textLabel.font = [UIFont fontWithName:#"Helvetica" size:17.0];
cell.textLabel.text = [arryTableIconsText objectAtIndex:indexPath.row];
cell.imageView.image = [UIImage imageNamed:[arryTableIcons objectAtIndex:indexPath.row]];
return cell;
}
The result is the following:
But I don't know where to go from here to convert it in to a checkbox to the right of each cell with the ID saved?
Any tips really will be appreciated, Tom
Use an NSMutableIndexSet instance variable and populate it with the index of the cells being checked.
Then in the cellForRowAtIndexPath method, set the accessory type of the cell to UITableViewCellAccessoryTypeCheckmark or UITableViewCellAccessoryTypeNone depending on whereas the indexPath.row is in the NSMutableIndexSet or not.
Finally, when the cell is tapped, add the indexPath.row to the indexset if not alread, or remove it if it already was present, to toggle the status of the corresponding cell, then call reloadData on the tableView.
I see in your code too that you are not familiar with the reuse mechanism of UITableViewCells. You should read the "Table View Programming Guide" in Apple's documentation and learn how to implement cellForRowAtIndexPath in a more efficient and reactive way (in term of reactivity and memory footprint)
Example
// Let selectedCellIndexes be an instance variable in your .h of type NSMutableIndexSet*
// Initialize it (probably at the same place you initialise your texts & icons, once for all, probably in your init method
selectedCellIndexes = [[NSMutableIndexSet alloc] init];
Then to fill the cells:
-(UITableViewCell*)tableView:(UITableView*)tv cellForRowAtIndexPath:(NSIndexPath*)indexPath {
// Try to recycle and already allocated cell (but not used anymore so we can reuse it)
UITableViewCell* cell = [tv dequeueCellWithReuseIdentifier:...];
if (cell == nil) {
// If we didn't manage to get a reusable (existing) cell to recycle it
// then allocate a new one and configure its general properties common to all cells
cell = [[[UITableViewCell alloc] initWithStyle:... reuseIdentifier:...] autorelease];
// ... configure stuff that are common to all your cells : lineBreakMode, numberOfLines, font... once for all
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.numberOfLines = 0;
cell.textLabel.font = [UIFont fontWithName:#"Helvetica" size:17.0];
}
// Then here change the stuff that are different between each cell
// (this code will be executed if the cell has just been allocated as well as if the cell is an old cell being recycled)
cell.textLabel.text = [arryTableIconsText objectAtIndex:indexPath.row];
cell.imageView.image = [UIImage imageNamed:[arryTableIcons objectAtIndex:indexPath.row]];
cell.accessoryType = [selectedCellIndexes containsIndex:indexPath.row] ? UITableViewCellAccessoryTypeCheckmark : UITableViewCellAccessoryTypeNone;
return cell;
}
And finally, to toggle the checkmarks:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ([selectedCellIndexes containsIndex:indexPath.row]) {
[selectedCellIndexes removeIndex:indexPath.row];
} else {
[selectedCellIndexes addIndex:indexPath.row];
}
[tableView reloadData];
}

Clear all UITextField changes in a UITableView with a UIButton

I have a UITableView with a dozen rows, each containing a UITextField.
By default the UITextField contains a placeholder value "Add Value" if the user hasn't previously edited the text field:
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(158, 6, 148, 24)];
NSString *strReplacement = [valueArray objectAtIndex:indexPath.row];
if (([strReplacement length] != 0) {
textField.text = strReplacement;
} else {
textField.placeholder = #"Add Value";
}
textField.delegate = self;
[cell addSubview:textField];
[textField release];
So far so good.
I've also added a UIButton to the footer of the UITableView.
What I want is to clear all the edited values and refresh all the UITextFields in the UITableView when the user clicks the UIButton.
I can easily enough remove all objects from the valueArray but I can't figure out how to refresh all the UITableView cells to reflect the changes.
Any help is appreciated.
lq
I believe what you're looking for is
[tableView reloadData];
Your solution feels weird. Filipe's right that the correct way to do it is with [wordsTableView reloadData], which will cause tableView:cellForRowAtIndexPath: to be called for each visible cell. That method is also called as you scroll through the table, so if reloadData isn't working, you're probably also going to end up with bugs with data not updating correctly as you change it and scroll. In your clearValues method, you're doing the same thing by calling tableView:cellForRowAtIndexPath:.
I think the real problem is in your tableView:cellForRowAtIndexPath: implementation. That method generally has 2 sections. First, you create or recycle a cell to get a reference with something like:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
Inside that if statement is generally the only place you should be adding subviews to your cell. If dequeueReusableCellWithIdentifier: returns a cell, it should already have the subview.
Then, after that if statement, you populate or update the contents of the subviews. The problem with your original code is that it's populating the text field and adding it as a subview, assuming there isn't already a text field in the cell. So your tableView:cellForRowAtIndexPath: should look something more like this:
int textFieldTag = 100;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(158, 6, 148, 24)];
[textField setTag:textFieldTag];
[textField setDelegate:self];
[cell addSubview:textField];
[textField release];
}
UITextField *textField = [cell viewWithTag:textFieldTag];
NSString *strReplacement = [valueArray objectAtIndex:indexPath.row];
if (([strReplacement length] != 0) {
textField.text = strReplacement;
} else {
textField.placeholder = #"Add Value";
}
It looks like you may be setting the textField's tag value to the row number, presumably so you can use it in the UITextFieldDelegate. That could also lead to bugs, as if the cell from row 1 is recycled by dequeueReusableCellWithIdentifier: and becomes row 12, it's going to have an unexpected tag value. Even if it doesn't happen now, it's a bug waiting to happen, and will be tricky to troubleshoot.
Filipe's solution should also work, however, calling reloadData should be avoided wherever possible as calling this method has a high performance overhead.
You need some class that has a reference to both the UIButton instance as well as all the instances of the UITextField that you have on the screen/in the table. Sounds like the perfect job for your UITableView controller subclass!
In your code above, why don't you also add each UITextField that you create to an NSArray of text fields that lives in your UITableView controller? Then when the user presses the UIButton, the action can call some method in your controller class, which loops through all the UITextField elements in the NSArray setting the text property of each instance to #"".
Warning: If you're reusing cells then you may have to ensure that the controller's NSArray of UITextFields is being updated properly.
After a few hours of trial and error, I came up with this solution. The UIButton "Clear All" invokes the following method:
- (IBAction)clearValues:(id)sender {
// count the number of values in the array (this is the same as the number of rows in the table)
int count = [valueArray count];
// remove all values from the array (deletes any user added values):
[self.valueArray removeAllObjects];
UITableViewCell *cell;
UITextField *textField;
// loop through each row in the table and put nil in each UITextField:
for (NSUInteger i = 0; i < count; ++i) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
cell = [self.wordsTableView cellForRowAtIndexPath:indexPath];
// NOTE: Make sure none of your tags are set to 0 since all non-tagged objects are zero.
// In table construction, your textFieldTags should be: textField.tag=indexPath.row+1;
textField = (UITextField*)[cell viewWithTag:i+1];
textField.text = nil;
}
}