有几件事:表格视图没有责任记住每个单元格中的内容。它在滚动时丢弃单元格,并要求数据源通过 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;
}