Почему эта строка заливает мой tableview? — cell = [self.TableView dequeueReusableCellWithIdentifier:идентификатор ячейки];

#iphone #uitableview #delegates

#iPhone #uitableview #делегаты

Вопрос:

Когда я комментирую эту строку в своей cellForRowAtIndexPath: функции:

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

..затем все ячейки восстанавливаются каждый раз и не используются повторно… Это приводит к тому, что все текущие значения моей ячейки tableview устанавливаются идеально. Однако в моих ячейках должны отображаться старые и новые значения. Конечно, поскольку ячейка всегда выделяется, все исторические значения теряются.

..Когда я оставляю эту строку там и прокручиваю ее до нижней части моего tableview, значения в ячейках внизу таблицы будут содержать некоторые значения, которые принадлежат ячейкам вверху… Я что-то перепутал.

Короче говоря, у меня есть UITableView пользовательский UITableViewCell s. Пользовательские ячейки имеют следующее:

  • элемент UIStepper управления
  • для id delegate сохранения вызова tableView , чтобы я мог запускать методы в tableView .
  • свойства, позволяющие каждой ячейке сохранять свой раздел и строку в tableview

Постановка проблемы:
когда я загружаю tableview и пользователь нажимает на UIStepper элемент управления в ячейке, он вызывает функцию делегирования doit: обратно в tableView реализацию. В этой функции свойства раздела и строки пользовательской ячейки работают отлично, и они идеально указывают на нужный раздел и строку в источнике данных (и tableView ), но stepper.value значение «каким-то образом» устанавливается на значение stepper.value из другой ячейки в таблице.

Кроме того, я действительно замечаю эту аномалию только тогда, когда прокручиваю значительные расстояния в таблице … т.е. сверху вниз. Нижняя ячейка stepper.value будет принадлежать ячейке, которая находилась в верхней части таблицы. Итак, я думаю, что столкнулся с осложнением, связанным с тем, как ячейки кэшируются и т. Д…

Я включил код из класса CustomCell, а также из родительского TableView.

Кто-нибудь видит ошибку в моих способах? Большое спасибо.

Вот файл customTableViewCell заголовка.

Возвращаясь к фактическому TableView, где я создаю пользовательские ячейки, я вызываю метод пользовательской ячейки setDelegate: (показан ниже), чтобы иметь возможность понять, какая ячейка в таблице была нажата.

«Звездой» шоу является элемент управления UIStepper, который позволяет пользователю «увеличивать» или «уменьшать» значение оценки, которое также отображается в ячейке.

 #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
  

Here is the important part of the customTableViewCell .m file:

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

Вот TableView, в котором находятся объекты customViewCell.

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

Вот где у меня проблема. Смотрите Описание проблемы в коде ниже.

 - (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. Вы отметили все свои свойства как assign @property (nonatomic, assign) IBOutlet UILabel *score; . Более правильный подход — пометить его как retain .

2. Спасибо, бериллий. Но я изменил все на «сохранить» и никакой радости. Вот обновление к постановке проблемы. Когда я комментирую строку ниже в моем TableView, ячейки не используются повторно, и UIStepper.value является правильным. Но это нарушает мою способность сохранять исходное значение «оценка» ячейки, поскольку каждая ячейка обновляется каждый раз. (Я показываю старые и новые значения в ячейках). Вот строка, на которую я ссылаюсь выше: cell = (tvcCustomCellWithSetter *)[self.TableView dequeueReusableCellWithIdentifier:cellIdentifier];

Ответ №1:

Чтобы предотвратить 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
}
  

Как только вы это сделаете, вы сможете выполнить дальнейшую отладку, чтобы проверить, не установлены ли ваши значения неправильно (если значение равно нулю, когда должно быть иначе, вы знаете, что не удается установить правильное значение для этой конкретной ячейки, а затем поместите точки останова в код и посмотрите на переменнуюзначения и т. Д.) .

РЕДАКТИРОВАТЬ: я буду более конкретным. При отладке вы увидите, что при пошаговом просмотре кода:

  • Значения шагового параметра явно устанавливаются на значения по умолчанию до установки правильного значения или
  • Значения явно не установлены по умолчанию (что означает, что ваш код сброса значений не работает).

И затем:

  • Значение шага устанавливается на правильное значение для текущей ячейки, или
  • Значение шагового параметра устанавливается на значение, ОТЛИЧНОЕ от ожидаемого (в этом случае вам нужно отследить код в точке, где он устанавливает это значение, и посмотреть, какие индексы и какое значение он в конечном итоге выбирает и т. Д.), Или
  • Значение шагового параметра вообще не было изменено по сравнению с его значением по умолчанию (что означает, что код для установки падает, или есть необычный случай, когда вы получаете nil , 0 или просто не выполняется код для установки значения, и вам нужно отследить и посмотреть, куда идет код, чтобы добраться до этогоэтап).

Это должно быть вашей стандартной процедурой отладки.

Комментарии:

1. Спасибо, дарвидсОн, установлю это и попробую отладку точки останова. Возвращаюсь к вам через: 20m.

2. Нет, никакой радости. Я действительно застрял на этом. Есть другие предложения?

Ответ №2:

В итоге я решил эту проблему, ударившись головой о стену и создав подкласс класса UIStepper, чтобы добавить свойство row и section, которое позволило мне определить, какая ячейка tableview представляла элемент управления при нажатии. Я также использовал метод «SetTarget», размещенный в каждой ячейке во время метода cellforrowatindexpath(), он вызывал бы функцию в моем классе tableview всякий раз, когда значение менялось в UIStepper. В этой функции я смог использовать значения строк и разделов из customStepper, чтобы точно определить строку в таблице и, следовательно, источник данных, который необходимо обновить.

Фух.