为什么这条线路会影响我的桌面视图? - cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

时间:2011-10-30 20:33:00

标签: iphone uitableview delegates

当我在cellForRowAtIndexPath:函数中注释掉这一行时:

cell = (tvcCustomCellWithSetter *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 

..然后每次都重新生成所有单元格而不重复使用...这会导致我的所有tableview单元格当前值都被完美设置。但是,我的单元格需要显示旧值和新值。当然,由于单元格总是被分配,所以所有的历史值都会丢失。

..当我在那里留下那一行,并滚动到我的tableview的底部时,表格底部的单元格中的值将包含属于顶部单元格的一些值...我我得到了一些混乱的东西。

简而言之,我有UITableView由自定义UITableViewCell组成。自定义单元格具有以下内容:

  • UIStepper控件
  • id delegate存储调用tableView,以便我可以在tableView中运行方法。
  • 允许每个单元格在tableview中存储其section和row的属性

问题陈述:
当我加载tableview并且用户点击单元格中的UIStepper控件时,它会在doit:实现中调用委托函数tableView。在该函数中,自定义单元格的节和行属性运行良好,它们完美地指向数据源中的右侧部分和行(以及tableView),但stepper.value被设置为“某种方式”到来自表格中另一个单元格的stepper.value

另外,当我在表格中滚动很长的距离时,我才真正注意到这种异常...即。从上到下。底部单元格的stepper.value将是靠近表格顶部的单元格的customTableViewCell。所以我认为我遇到了一个源于细胞缓存方式的并发症等等......

我已经包含了来自customCell类以及父TableView的代码。

有没有人以我的方式看到错误?非常感谢。

这是setDelegate:头文件。

回到实际的TableView,我实例化自定义单元格,我调用自定义单元格的#import <UIKit/UIKit.h> @interface tvcCustomCellWithSetter : UITableViewCell { id delegate; NSNumber * row; NSNumber * section; int originalValueIsSet; } @property (nonatomic, assign) IBOutlet UILabel *score; @property (nonatomic, assign) IBOutlet UILabel *catAndWeight; @property (nonatomic, assign) IBOutlet UILabel *orgValueText; @property (nonatomic, assign) IBOutlet UILabel *orgValueValue; @property (nonatomic, assign) IBOutlet UIStepper *theStepper; @property (nonatomic, assign) id delegate; @property (nonatomic, assign) NSNumber *row; @property (nonatomic, assign) NSNumber *section; @property (nonatomic, assign) int originalValueIsSet; - (IBAction) bumpTheScore; - (id)delegate; - (void)setDelegate:(id)newDelegate; @end 方法(如下所示),以便能够理解表格中的哪个单元格被点击。

该节目的“明星”是UIStepper控件,允许用户“向上”或“向下” 得分值,也显示在单元格上。

//
// here is my IBACTION function associated with a touch event on the UISetter control
//
-(void) bumpTheScore {

    [self.delegate doit:self];

}


- (id)delegate {
    return delegate;
}

// 
// this function is called when the cell is alloc'd over in the Tableview controller.
// it allows this custom cell to maintain a reference to the 
- (void)setDelegate:(id)newDelegate {
    delegate = newDelegate;
}

以下是customTableViewCell .m文件的重要部分:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    tvcCustomCellWithSetter *cell = nil;

    cell = (tvcCustomCellWithSetter *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"customCell" owner:self options:nil];
        for(id currentObject in topLevelObjects){
            if([currentObject isKindOfClass:[tvcCustomCellWithSetter class]]) {
                cell = (tvcCustomCellWithSetter *)currentObject;
                break;
            }
        }


    } 

    // since the cell is a custom cell and it lives in a new class file, when events 
    // occur from toys in that cell (i.e. sliders, stepper, etc), the events go to that
    // class file.  I want those events to go to the simgrades class and run a reactive
    // function in there.  this is how you do that.   see the two functions in the 
    // custom class that work with this line.
    [cell setDelegate:self];

    // similarly, save the current section and row into the custom cell itself
    // so that later, when the stepper is clicked on one of the cells, we'll be able
    // to know the location in the datasource that is being addressed by the user.
    [cell setRow: [NSNumber numberWithInt: indexPath.row]];
    [cell setSection: [NSNumber numberWithInt: indexPath.section]];      

    // Set up the cell...
    [self.tableView setAllowsSelectionDuringEditing:NO];
    SimCat *tmpCat = [simCatalog objectAtIndex: indexPath.section];
    SimGrade *tmpGrd = [[tmpCat simGrades] objectAtIndex:indexPath.row];

    float averageScore, _score, _total;
    _score = [tmpGrd.scoreValue floatValue];
    _total = [tmpGrd.scorePossible floatValue];
    averageScore = (_score / _total) * 100;

    // Configure the cell...  Category name and description
    if (tmpGrd.isSimGrade) {
        cell.score.text = [NSString stringWithFormat:@"SimGrade: %2.2f%% - (%@ of %@)", averageScore, [tmpGrd.scoreValue stringValue] , [tmpGrd.scorePossible stringValue]];
        //for simulation grades, null out the orgValue labels.
        cell.orgValueValue.text = [NSString stringWithString: @""];
        cell.orgValueText.text = [NSString stringWithString: @""];
    } else {
        cell.score.text = [NSString stringWithFormat:@"Actual: %2.2f%% - (%@ of %@)", averageScore, [tmpGrd.scoreValue stringValue] , [tmpGrd.scorePossible stringValue]];
        //set the orig value label and turn on the boolean that shows that you've set this already.
        if (! cell.originalValueIsSet ) {
            cell.orgValueValue.text = [NSString stringWithFormat:@"%@", [tmpGrd.scoreValue stringValue]];
            cell.orgValueText.text = [NSString stringWithString: @"Original Value:"];
            cell.originalValueIsSet = true;
        }
    }

    cell.catAndWeight.text = [NSString stringWithFormat:@"Category: %@, Wt: %d", tmpCat.catName, [[tmpCat catWeight] intValue]];

    [cell.theStepper setValue:[tmpGrd.scoreValue floatValue]];
    [cell.theStepper setMaximumValue:[tmpGrd.scorePossible floatValue]];
    [cell.theStepper setMinimumValue:0];

    return cell;
}

这是包含customViewCell对象的TableView。

- (void) doit: (id) sender {

    NSLog(@"it worked - i got back inside the tableview that houses the custom cells");
    NSLog(@"the user touched the UISetter control on a cell in section: %d", [[(tvcCustomCellWithSetter *)sender section] intValue]);
    NSLog(@"and that cell was in row: %d of that section", [[(tvcCustomCellWithSetter *)sender row] intValue]);

    // find the right section and row in the tableview's data source
    // point to the section indicated by the "section" passed in from the custom cell.
    SimCat *cat = [simCatalog objectAtIndex:[[(tvcCustomCellWithSetter *)sender section] intValue] ];

    // now point at the array item that corresponds to the "row" passed in from the custom cell
    SimGrade *grade = [[cat simGrades] objectAtIndex:[[(tvcCustomCellWithSetter *)sender row] intValue]];

    // now that we are pointing at the right array item in the tableview's datasource, update it's value to that of the UIStepper
    // control's value.
    // THE PROBLEM OCCURS IN THE FOLLOWING LINES.  The before value that is printed out is perfect.  So I know I am able to 
    // reference the correct value in the datasource that the user has just incremented or decremented with the UIStepper
    // control.  The "theStepper.value" should contain a number that is one more or one less than the previous value...
    // HOWEVER, the value stored in theStepper.value is actually a value from one of the other cells in the table.
    // I'm thinking that this has to do with the way the table cells are cached.  I usually see this error when I scroll to the
    // bottom of the tableview.  

    NSLog(@"before: %f", [[grade scoreValue] floatValue]);
    grade.scoreValue = [NSNumber numberWithFloat: [[(tvcCustomCellWithSetter *)sender theStepper] value]];
    NSLog(@"after: %f", [[grade scoreValue] floatValue]);

    // you have changed some values in the Tableview's datasource so you should repaint the view.
    [self loadHTMLHeader];
    [[self tableView] reloadData];

    return;
}

这是我遇到问题的地方。请参阅以下代码中的问题说明。

{{1}}

2 个答案:

答案 0 :(得分:0)

要防止theStepper值继续存在,如果dequeueReusableCell..方法返回一个单元格,则需要将它们显式设置为默认值。

所以:

cell = (tvcCustomCellWithSetter *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) {
    NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"customCell" owner:self options:nil];
    for(id currentObject in topLevelObjects){
        if([currentObject isKindOfClass:[tvcCustomCellWithSetter class]]) {
            cell = (tvcCustomCellWithSetter *)currentObject;
            break;
        }
    }
} else {
    cell.theStepper.value = 0;
    cell.theStepper.maximumValue = 1;
    // cell.theStepper.minimumValue = 0; this should already be set properly
}

一旦你这样做,那么你可以做进一步的调试,看看你的值是否没有正确设置(如果值为零,否则,你知道它没有为那个特定的单元格设置正确的值,然后在代码中放置断点并查看变量值等。)

编辑:我会更具体。从调试开始,您将逐步查看代码:

  • 在设置正确的值之前,步进器值显式设置为默认值,或
  • 这些值未显式设置为默认值(这意味着您的值重置代码不起作用)。

然后:

  • 步进器值设置为当前单元格的正确值,或
  • 步进器值设置为与预期值不同的值(在这种情况下,您需要在设置该值的位置跟踪代码并查看索引是什么以及最终选择哪个值等)或者
  • 步进器值完全没有根据其默认值进行更改(这意味着要设置的代码正在崩溃,或者有一个不寻常的情况,您最终会得到nil0或者只是没有执行代码来设置值,您需要跟踪并查看代码到达该阶段的位置。)

这应该是您的标准调试程序。

答案 1 :(得分:0)

我最后通过击败墙壁并继承UIStepper类来解决这个问题,以便添加一个行和节属性,使我能够确定在单击时呈现控件的是哪个tableview单元格。我还使用了一个“setTarget”方法,在cellforrowatindexpath()方法中放置在每个单元格上,只要在UIStepper上更改了值,它就会调用我的tableview类中的函数。在该函数中,我能够利用customStepper中的行和节值来精确定位表中的行,从而确定需要更新的数据源。

呼。