Reputation: 26526
I have a UITableView
tall enough that it necessitates scrolling. The top-most cell in the table contains a UITextField
for the user to enter some text.
The standard way to build this might be to create and add the text field and add it to a cell created or recycled in cellFOrRowAtIndexPath:
However, this constant re-creation means that the text entered in the field is erased when the cell is scrolled out and back into view.
The solutions I've found so far suggest using UITextField
delegation to track the text as it changes and store it in an iVar or property. I would like to know why this is recommended instead of the simpler approach I am using:
I am creating the UITextField
in the init method of the UITableViewController
and immediately storing it in a property. In cellFOrROwAtIndexPath
I am simply adding the pre-existing field instead of initializing a new one. The cell itself can be recycled without issue, but because I am always using the one and only UITextField
, the content is maintained.
Is this a reasonable approach? What might go wrong? Any improvements (perhaps I could still create the field in cellForRowAtIndexPath
but first check if the property is nil?)
Upvotes: 4
Views: 1504
Reputation: 3701
#import "ViewController.h"
#import "TxtFieldCell.h"
#define NUMBER_OF_ROWS 26
@interface ViewController ()<UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate>
@property (weak, nonatomic) IBOutlet UITableView *tablView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tablView.datasource = self; //set textfield delegate in storyboard
textFieldValuesArray = [[NSMutableArray alloc] init];
for(int i=0; i<NUMBER_OF_ROWS; i++){
[textFieldValuesArray addObject:@""];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#pragma mark - TableView Datasource
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TxtFieldCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TxtFieldCellId" forIndexPath:indexPath];
cell.txtField.tag = indexPath.row;
if (textFieldValuesArray.count > 0) {
NSString *strText = [textFieldValuesArray objectAtIndex:indexPath.row];
cell.txtField.text = strText;
}
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return NUMBER_OF_ROWS;
}
#pragma mark - TextField Delegate
- (void)textFieldDidEndEditing:(UITextField *)textField {
[textFieldValuesArray replaceObjectAtIndex:textField.tag withObject:textField.text];
}
Upvotes: 0
Reputation: 131
When you are creating cells in cellForRowAtIndexPath
you have to use one reusable identifier for that first cell (ie. cellId1) and another for the rest (ie. cellId2).
If you do this, when you get the cell for the first element by calling [tableView dequeueReusableCellWithIdentifier:@"cellId1"]
you will always get the same Object and will not be reused by other cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = nil;
// Only for first row
if (indexPath.row == 0) {
static NSString *cellId1 = @"cellId1";
cell = [tableView dequeueReusableCellWithIdentifier:cellId1];
if (cell == nil) {
cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId1];
}
}
else {
static NSString *cellId2 = @"cellId2";
cell = [tableView cellId2];
if (cell == nil) {
cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleDefault cellId2];
}
}
// do whatever
return cell;
}
Upvotes: 1
Reputation: 4077
If there is only one UITextField, then I agree that your approach would be better/same as compared to using UITextField delegation (I think).
However, let us assume that you want to "expand" your view so that there are about 7-8 or more TextFields now. Then if you go about using your approach, then the problem will be that you will be storing 7-8 or more TextFields in memory and maintaining them.
In such a situation, a better approach would be that you create only that number of textfields as visible on screen. Then you create a dictionary which would maintain the content present in the textfield (which you can get by UITextFieldDelegate methods). This way, the same textfield can be used when the cell is reused. Only the values will change and will be dictated by the values in the dictionary.
On a sidenote, do minimal creation in cellForRowAtIndexPath as that is called during every table scroll and so creating a textField in cellForRowAtIndexPath can be expensive.
Upvotes: 0