0

I have a UITableView in which I have a custom prototype cell, defined in another class (CustomCell), with a UITextField in it. Every time I press a button, it calls a method called addItem, which creates a new cell. I want the texts in the UITextFields to go to an array. To try to explain it better, if I add 3 cells to the UITableView and input 3 texts in the corresponding UITextFields, I want the text in 1st cell to go to the array in index 0, the text in the 2nd to go to index 1 and the text in 3rd cell to go to index 2. My biggest problem is that I want to be able to go back to UITextField in cell 1 and update it, and have it dynamically update the NSArray object corresponding to it, that is, the one at index 0. I have no idea how to approach it. Can anybody help??? Thank you very much!!

my code (obs: itemTable is the UITableView):

MainViewController.m

@implementation addViewController{
    NSInteger n;
    NSString *aid;
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

-(void)viewWillAppear:(BOOL)animated{
    n=1;
    aid=@"";
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{

    return 1;
}


-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{

    return n;


}

- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    static NSString *CellIdentifier= @"Cell";

    CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }

    cell.itemNumber.text=[NSString stringWithFormat:@"Item %d",indexPath.row];

    return cell;

}
- (IBAction)addItem:(UIButton *)sender {
    ++n;
    [_itemTable reloadData];
}
- (IBAction)removeItem:(UIButton *)sender {
    if (n>=0)--n;
    [_itemTable reloadData];
}

CustomCell.m:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {_itemValue = [[UITextField alloc]init];

        _item = [[UILabel alloc]init];

        [self.contentView addSubview:_itemValue];

        [self.contentView addSubview:_item];

    }
    return self;
}

CustomCell.h

@interface CustomCell : UITableViewCell
@property (strong, nonatomic) IBOutlet UILabel *itemNumber;
@property (strong, nonatomic) IBOutlet UITextField *itemValue;

@end
Fabio
  • 3,015
  • 2
  • 29
  • 49
  • Were you also asking about how to add cells on the button action? Otherwise, please consider marking @matt correct. – danh Apr 03 '13 at 04:57

4 Answers4

3

First, when you create each text field, you make yourself that text field's delegate, so you will get messages whenever something happens in the text field.

Okay, so now when the user types in a text field, you will get messages, and you can modify your model (the array, which you should keep as an NSMutableArray I suppose). But to do that, you need to figure out which the heck cell contains the text field that this message is coming from! You will do that something like this:

- (void)textFieldDidEndEditing:(UITextField *)tf {
    // some cell's text field has finished editing; which cell?
    UIView* v = tf;
    do {
        v = v.superview;
    } while (![v isKindOfClass: [UITableViewCell class]]);
    CustomCell* cell = (CustomCell*)v;
    // so which row is it?
    NSIndexPath* ip = [self.tableView indexPathForCell:cell];
    //  aha! so now ip.row is the row, and you can update your data model
    //   ... left as an exercise for the reader ...
}

I do exactly this sort of thing in my book, in http://www.apeth.com/iOSBook/ch21.html#_editable_content_in_table_items (that's where the above code comes from), so take a look and see what ideas it gives you.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • could I use that in the - `(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string` method?? – Fabio Apr 03 '13 at 02:10
  • Spelunking the view hierarchy in this case is not a very safe way to approach the problem. The tableview controller should be responsible for determining which cell is being edited and update the datasource accordingly. – Dan Fairaizl Apr 03 '13 at 02:10
  • @BasicallyBits that text delegate method is presumably implemented by the view controller containing the table. I guess walking up superviews is spelunking, but how might it be unsafe? Can't we count on view always being contained by superviews, now and in the future? – danh Apr 03 '13 at 04:54
  • You just shouldn't always depend on a UITextField to have a UITableViewCell as one of it's superviews. This approach is necessarily wrong, it's just a lot more fragile and prone to break if the implementation changes. – Dan Fairaizl Apr 03 '13 at 18:24
1

When the user is done entering text you could do something like the following which maps the index paths of the rows in your tableview to the indices in an array.

- (NSMutableArray *)updateText {

    NSUInteger cellsCount = [self.tableView numberOfRowsInSection:0];
    NSMutableArray *cellTextArray = [[NSMutableArray alloc] initWithCapacity:cellsCount];

    for(NSInteger i = 0; i < cellsCount; i++) {

        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        CustomCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];

        NSString *item = cell.itemNumber.text;

        [cellTextArray insertObject:item atIndex:i];
    }

    return cellTextArray;
}

Assuming your cell has the UITextFieldDelegate set, when the user is done entering text you can do something like this:

- (void)textFieldDidEndEditing:(UITextField *)textField {

    [self.delegate didFinishEditing];
}

Where self.delegate is the UITableViewController, which in turn call updateText when necessary.

Things to be careful of - the for loop in updateText needs to loop over the tableview and dequeue cells for each index path. Simply using the tableview's visible cells would most likely leaving you missing text from cells that were off screen and got reused.

Hope this helps and good luck!

Dan Fairaizl
  • 2,172
  • 2
  • 28
  • 31
0

There are obviously a few aspects of this problem. First of all, you want to be able to recover references to the UILabel's, so that you can figure out which row a specific UILabel is in. I'd recommend doing this using the tag property, like this:

_item = [[UILabel alloc] init];
_item.tag = 100; // or any value
[self.contentView addSubview:_item];

You also need to set an action that gets called whenever the text in the label gets changed. You can do that like this:

[_item addTarget:someObject
          action:@selector(labelChanged:)
forControlEvents:UIControlEventEditingChanged];

Whatever class someObject is, it needs to have a method with this signature:

- (void)labelChanged:(id)sender;

Inside that method you can check that sender is in fact a UILabel, and then you can access the new text with sender.text.

In order to figure out what point in the array to put the text in, you can declare a loop over the number of rows in your table:

for (int i = 0; i < [mainViewControllerInstance tableView:theTableInQuestion numberOfRowsInSection:0]; ++i) {
    if(sender == [[mainViewControllerInstance tableView:theTableInQuestion
                                 cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]] viewWithTag:100]) {
        // Put `sender.text` in the appropriate spot in your array
    }
}

A few additional notes: I'd use an NSMutableArray to keep track of your strings, since you'll be updating them, but I'm not entirely sure what best practices are here. You'll also want to make sure you initialize your array (whether you make it an NSArray or an NSMutableArray) to have the proper number of rows, and make sure that you add rows to it when the + button is pressed, or you'll risk getting an exception when you try to change an entry for a row that doesn't yet exist.

drewmm
  • 737
  • 6
  • 17
0

You might also want to have a look at the free Sensible TableView framework. The framework performs almost all what you need automatically. Should probably save you a lot of manual work. Good luck!

Matt
  • 2,391
  • 2
  • 17
  • 18