我正在创建一个可以添加产品信息的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的文本字段中插入的文本消失了。
有人知道如何防止这种情况吗?
答案 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返回单元格时使用它来填充单元格的内容。