0

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

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

- (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;
    }

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

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

4

2 回答 2

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 中被多次询问和回答(例如这里)。简而言之,最常见的解决方案是让视图控制器成为单元格中文本字段的代表。然后,在 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];

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

   - (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;
}
于 2013-05-25T19:19:22.947 回答
0

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

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

于 2013-05-25T17:25:40.767 回答