如何在添加/删除UITableView行时防止文本字段变空?

时间:2013-05-25 17:17:33

标签: objective-c uitableview uitextfield

我正在创建一个可以添加产品信息的UITableView。在每一行中,用户可以添加有关产品的信息,显然,用户可以自己设置行数。

用户可以通过点击NavigationBar中的“添加行”或“删除行”按钮一次添加或删除一行。这是它的工作原理:

- (void)viewDidLoad
{
    [super viewDidLoad];

    tableRows = [NSNumber numberWithInt:12];

}

-(void) addRow
{
    NSNumber *addRow =[NSNumber numberWithInt:1];
    tableRows= [NSNumber numberWithInt:(tableRows.intValue + addRow.intValue)];
    [self.tableView reloadData];

    NSLog(@"%@", tableRows);
}

-(void) removeRow
{
    NSNumber *addRow =[NSNumber numberWithInt:1];
    tableRows= [NSNumber numberWithInt:(tableRows.intValue - addRow.intValue)];
    [self.tableView reloadData];

    NSLog(@"%@", tableRows);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{

    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return tableRows.intValue;
}

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

        static NSString *CustomCellIdentifier = @"CustomCellIdentifier ";
        CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier];
        if (cell == nil) {
            NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell"
                                                         owner:self options:nil];
            for (id oneObject in nib) if ([oneObject isKindOfClass:[CustomCell class]])
                cell = (CustomCell *)oneObject;
            cell.selectionStyle = UITableViewCellSelectionStyleNone;

        }
        NSUInteger *row = [indexPath row];

        return cell;
    }

编辑工作完美,但当我添加或删除一行时,我在tableview的文本字段中插入的文本消失了。

有人知道如何防止这种情况吗?

2 个答案:

答案 0 :(得分:3)

一些事情:表格视图没有责任记住每个单元格中的内容。它会在滚动时丢弃单元格,并要求数据源通过cellForRowAtIndexPath再次初始化它们。 Reloaddata(您在添加/删除方法中使用)将使表刷新所有可见单元格。不要指望您的表中出现任何未在cellForRowAtIndexPath中设置的内容。

接下来,此表的“模型”是NSNumber“tableRows”,表示行数。这是表视图的不足模型。用NSMutableArray替换它。至少,此数组应包含表示每个文本字段状态的字符串。 (它可能需要更复杂的对象,但从字符串开始)。

这样,你的视图控制器类看起来会更像......

// this is your table's model
@property (nonatomic, strong) NSMutableArray *rows;

// in init for the class
_rows = [NSMutableArray array];

// somewhere else, put some data in it
[self.rows addObject:@"Foo"];
[self.rows addObject:@"Bar"];

现在您的数据源方法:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // Return the number of rows in the section.
    return self.rows.count;
}

然后,在cellForRowAtIndexPath:

NSUInteger *row = [indexPath row];   // as you have it
NSString *rowText = self.rows[row];  // this is new syntax, the old way is [self.rows objectAtIndex:row];

// your CustomCell needs to provide a way to get at the textField it contains
// it might do this with an outlet or viewWithTag...
cell.myTextField.text = rowText;
return cell;

最后,单元格中的文本字段构成了特殊的挑战。如何在视图不滚动时保存当前状态。已经在SO(here, for example)中询问并回答了这个问题。简而言之,最常见的解决方案是使视图控制器成为单元格中文本字段的委托。然后,在textFieldDidEndEditing上,像这样在模型中保存textField的值......

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

    NSIndexPath *indexPath = [self indexPathOfCellWithSubview:textField];
    self.rows[indexPath.row] = textField.text;
}

// I think this is the best way to get the index path of a cell, given some subview it contains
- (NSIndexPath *)indexPathOfCellWithSubview:(UIView *)view {

    while (view && ![view isKindOfClass:[UITableViewCell self]]) {
        view = view.superview;
    }
    return [self.tableView indexPathForCell:(UITableViewCell *)view];
}

编辑假设模型不仅仅是一个字符串。这是您应用NSObject的自定义子类的地方。

// MyModel.h
@interface MyModel : NSObject

@property (strong, nonatomic) NSString *itemName;
@property (assign, nonatomic) CGFloat price;
@property (strong, nonatomic) NSString *imageFileName;
@property (strong, nonatomic) UIImage *image;

- (id)initWithItemName:(NSString *)itemName price:(CGFloat)price imageFileName:(NSString *)imageFileName;

- (NSString *)stringPrice;
- (void)setStringPrice:(NSString *)stringPrice;

@end

// MyModel.m
@implementation MyModel

- (id)initWithItemName:(NSString *)itemName price:(CGFloat)price imageFileName:(NSString *)imageFileName {

    self = [self init];
    if (self) {
        _itemName = itemName;
        _price = price;
        _imageFileName = imageFileName;
    }
    return self;
}

// override the image getter to "lazily" create and cache the image
// if the images are on the web, this will require a slighly more elaborate method
// employing NSURLConnection.
- (UIImage *)image {

    if (!_image) {
        _image = [UIImage imageNamed:self.imageFileName];
    }
    return _image;
}

// added these to show you how you can conveniently encapsulate other
// behavior, like type conversion or validation, though, real ones of these
// would probably use NSNumberFormatter
- (NSString *)stringPrice {

    return [NSString stringWithFormat: @"%.2f", self.price];
}

- (void)setStringPrice:(NSString *)stringPrice {

    self.price = [stringPrice floatValue];
}

现在您可以创建一个这样的并将其添加到您的表中。 (务必#import "MyModel.h"

[self.rows addObject:[[MyModel alloc] initWithItemName:@"Toaster" price:39.95 imageFileName:@"toaster.png"]];

包含该表的视图控制器或多或少保持相同(当您更改一个类并更改一个密切相关的类时,它会告诉您,您的OO设计可能非常好)。对于替换字符串的花式模型,我们需要更改cellForRowAtIndexPath ...

NSUInteger *row = [indexPath row];
MyModel *myModel = self.rows[row];

cell.itemNameTextField.text = myModel.itemName;
cell.priceTextField.text = [myModel stringPrice];
cell.imageView.image = myModel.image;

// additional OO idea: teach your cell how to configure itself and move the foregoing code there
// [cell configureWithModel:myModel];

return cell;

另一个编辑:我们可以教这个模型如何将自己发布到远程Web服务,如下所示:

- (void)post {
    NSString *hostStr = @"http://myhost/create_product.php";
    NSURL *url = [NSURL URLWithString:hostStr];

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";

    NSString *post =[NSString stringWithFormat:@"item_name=%@&price=%@",self.itemName, [self stringPrice];
    NSString *postEscaped = [post stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSData *postData = [postEscaped dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];

    [request setHTTPBody:postData];
    [request setValue:@"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:@"Content-Type"];

    [NSURLConnection sendAsynchronousRequest:request
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

                           if (!error) {
                               NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                               NSLog(@"response %@", string);
                           } else {
                               NSLog(@"error %@", error);
                           }
                       }];
}

在.h中声明此方法,根据需要将其他字段添加到帖子中(例如图像文件名等)

在您的视图控制器中,选择意味着用户想要提交新行的操作(也许是在文本字段完成编辑时?),并添加此...

// text field finished editing
MyModel *myModel = self.rows[indexPath.row];
myModel.itemName = textField.text;
[myModel post];

由于图像可能来自您的远程服务,因此您需要更改我之前添加的延迟加载图像获取器。加载此图像的正确方法是异步的,但这样做会使与表视图的交互变得太复杂,无法在此讨论。请参阅apple docs或this SO post以了解更多相关信息。与此同时,这是快速 - 但基本上是错误的 - 同步获取图像的方式......

   - (UIImage *)image {

    if (!_image) {
        // note - now the file name must be of the form @"http://host/path/filename.png"
        NSURL *url = [NSURL URLWithString:self.imageFileName
        _image = [UIImage imageWithContentsOfURL:url];
    }
    return _image;
}

答案 1 :(得分:0)

查看cellForRowAtIndexPath的代码会很有帮助,我们需要了解您打算存储数据的模型的更多信息。

从表中删除行时,该单元格将被抛出,并且tableview将不会自动记住内容。您必须在模型对象发生时保存更改,然后在从cellForRowAtIndexPath返回单元格时使用它来填充单元格的内容。