Какао-Touch - UITableView dequeueReusableCellWithIdentifier дает мне неправильные данные ячейки - PullRequest
0 голосов
/ 19 января 2012

Я пытаюсь использовать UITextField внутри UITableViewCell, как вы можете видеть в коде ниже.Кажется, что когда табличное представление выходит за пределы экрана, некоторые данные, которые должны быть в ячейках, смешиваются.Я думаю, что у метода [tableView dequeueReusableCellWithIdentifier:addGroupContactCellIdentifier]; возникает некоторая проблема, заключающаяся в том, что он не может дать мне "правильную" ячейку после того, как табличное представление исчезло с экрана.В чем причина этого?

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *addGroupContactCellIdentifier = @"AddGroupContactCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:addGroupContactCellIdentifier];

    if (cell == nil) {

        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault 
                                       reuseIdentifier:addGroupContactCellIdentifier];

        if ([indexPath section] == 0) { // Group Name Section

            cell.textLabel.text = @"Name";

            UITextField *groupNameTextField = [[UITextField alloc]initWithFrame:CGRectMake(80, 10, 210, 22)];
            groupNameTextField.textAlignment = UITextAlignmentLeft;
            groupNameTextField.backgroundColor = [UIColor clearColor];
            groupNameTextField.placeholder = @"Type Group Name";

            //groupNameTextField.borderStyle = UITextBorderStyleLine;
            groupNameTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
            groupNameTextField.returnKeyType = UIReturnKeyDone;
            groupNameTextField.autocapitalizationType = UITextAutocapitalizationTypeSentences;
            groupNameTextField.delegate = self;

            [cell.contentView addSubview:groupNameTextField];

        }

    }

    if ([indexPath section] == 1) { // Contacts Section

        cell.textLabel.text = [[self.selectedPeoplePickerContacts objectAtIndex:[indexPath row]] objectForKey:@"name"];
        cell.detailTextLabel.text = [[self.selectedPeoplePickerContacts objectAtIndex:[indexPath row]] objectForKey:@"number"];

    }

    cell.accessoryType = UITableViewCellAccessoryNone;

    return cell;    
}

ОБНОВЛЕНИЕ:

Итак, я вложил в подкласс UITableViewCell, но все равно он показывает ту же ошибку, что и раньше.Теперь это мой код для tableView:cellForRowAtIndexPath::

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *addGroupContactCellIdentifier = @"AddGroupContactCell";

    if ([indexPath section] == 0) {

        UITableViewCellWithUITextField *cell = [tableView dequeueReusableCellWithIdentifier:addGroupContactCellIdentifier];

        if (cell == nil) {

            //cell = [[UITableViewCellWithUITextField alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:addGroupContactCellIdentifier];

            cell = [[UITableViewCellWithUITextField alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:addGroupContactCellIdentifier textFieldPlaceholder:@"Type Group Name" textFieldDelegate:self];
        }

        cell.selectionStyle = UITableViewCellSelectionStyleNone;
        cell.textLabel.text = @"Name";

        // Need to set the UITableViewCell's textLabel properties otherwise they will cover the UITextField
        cell.textLabel.opaque = NO;
        cell.textLabel.backgroundColor = [UIColor clearColor];

        return cell;

    } else { 

        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:addGroupContactCellIdentifier];

        if (cell == nil) {

            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault 
                                          reuseIdentifier:addGroupContactCellIdentifier];
        }

        cell.textLabel.text = [[self.selectedPeoplePickerContacts objectAtIndex:[indexPath row]] objectForKey:@"name"];
        cell.detailTextLabel.text = [[self.selectedPeoplePickerContacts objectAtIndex:[indexPath row]] objectForKey:@"number"];

        return cell;
    }
}

Третье редактирование (теперь у меня есть 2 различных идентификатора reuseIdentifiers, которые, кажется, дают мне мои требуемые результаты):

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    if ([indexPath section] == 0) { // Group Name Section

        static NSString *groupNameCellIdentifier = @"GroupNameCell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:groupNameCellIdentifier];

        if (cell == nil) {

            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault 
                                          reuseIdentifier:groupNameCellIdentifier];

            cell.textLabel.text = @"Name";

            UITextField *groupNameTextField = [[UITextField alloc]initWithFrame:CGRectMake(80, 10, 210, 22)];
            groupNameTextField.textAlignment = UITextAlignmentLeft;
            groupNameTextField.backgroundColor = [UIColor clearColor];
            groupNameTextField.placeholder = @"Type Group Name";

            //groupNameTextField.borderStyle = UITextBorderStyleLine;
            groupNameTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
            groupNameTextField.returnKeyType = UIReturnKeyDone;
            groupNameTextField.autocapitalizationType = UITextAutocapitalizationTypeSentences;
            groupNameTextField.delegate = self;

            [cell.contentView addSubview:groupNameTextField];
        }

        // Customization


        return cell;

    } else {

        static NSString *addGroupContactCellIdentifier = @"AddGroupContactCell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:addGroupContactCellIdentifier];

        if (cell == nil) {

            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault 
                                          reuseIdentifier:addGroupContactCellIdentifier];
        }

        // Customization
        cell.textLabel.text = [[self.selectedPeoplePickerContacts objectAtIndex:[indexPath row]] objectForKey:@"name"];
        cell.detailTextLabel.text = [[self.selectedPeoplePickerContacts objectAtIndex:[indexPath row]] objectForKey:@"number"];

        return cell;
    }
}

Ответы [ 3 ]

1 голос
/ 21 января 2012

Подклассы не обязательны, как предлагали некоторые.

Но вы не можете использовать логику вроде "if ([indexPath section] == 0) {" внутри "if (cell == nil) {", потому что она вызывается только при первом создании ячейки, и это будет использоваться в других индексах при последующих циклах.

Вместо этого вам нужно использовать два разных CellIdentifiers, чтобы ячейки, которые вы установили для нулевого раздела, не использовались повторно в других местах таблицы. Поместите свой if ([indexPath section] == 0) {перед тем, как убрать ячейку из очереди и использовать разные идентификаторы ячеек для нулевой секции и последующих ячеек секции.

Кроме того, убедитесь, что вы делаете какие-либо специфичные для indexpath за пределами "if (cell == nil) {", чтобы оно применялось при каждом повторном использовании ячейки, а не только при первом создании.

0 голосов
/ 19 января 2012

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

ПОЯСНЕНИЯ

По сути, в вашем коде есть один главный концептуальный недостаток: после регенерации ячейки вы не конфигурируете контент должным образом (вы его вообще не конфигурируете). Я имею в виду, что изначально, когда таблица отображается в первый раз, пул пуст. Таким образом, каждая новая ячейка, которая должна отображаться, воссоздается с нуля (а не извлекается из пула); допустим, ваша таблица может отображать 10 ячеек на экране, поэтому первые 10 ячеек будут созданы с нуля с пустыми текстовыми полями. Затем вы начинаете вводить текст в эти поля, и все работает правильно. В определенный момент вы начинаете прокручивать ячейку: происходит то, что все ячейки, которые находятся вверху, исчезают с экрана и сохраняются (помещаются в очередь) в пуле таблиц с их текстовым полем и отредактированным содержимым; скажем, вы ставите ячейку в очередь в строке 0. Когда в нижней части экрана должна отображаться новая ячейка, первое, что делает ваш код, это пытается заблокировать ячейку. Теперь на этот раз у вас есть ячейка в пуле (ячейка, которая была в строке 0), эта ячейка извлекается из пула и помещается в таблицу, ВКЛЮЧАЯ СОДЕРЖАНИЕ ТЕКСТОВОГО ПОЛОЖЕНИЯ, в строке 11. Итак, «волшебным образом» вы найдете текст, отредактированный в строке 0 в другой строке, 11. Кроме того, ячейки извлекаются из пула в разреженном порядке, поэтому после многих редактирования и прокрутки текстового поля у вас будет полное смешение.

Решение, и это является причиной ошибки в вашем коде: как только ячейка будет создана или отключена, настройте ее, то есть задайте содержимое текстового поля. Как получить содержимое текстового поля? хранить в массиве. Вот почему ваш контроллер представления является «источником данных», потому что вы вводите данные для заполнения таблицы. Хранение данных в таблице является ошибкой из-за этого механизма блокировки. Пример:


groupNameTextField.text=[myTextFieldContentArray objectAtIndex:indexPath.row];

Другое решение, но я не предлагаю его, это назначить уникальный идентификатор каждой ячейке, а именно:


NSString *myCellId = [NSString stringWithFormat:@"CellID_%d_%d",indexPath.section,indexPath.row];

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

0 голосов
/ 19 января 2012

Вы правы! Проблема определенно связана с reusability функцией UITableView. Apple сделала это таким образом, чтобы вы могли повторно использовать ячейки, и это прекрасно работает с точки зрения производительности! И поэтому, когда вы пытаетесь прокрутить вверх и вниз, и значения indexPath остаются прежними, и ваш tableView получает данные из cellForRowAtIndexPath, который вы определили в своем классе!

Решение:

Вам нужно будет создать подкласс UITableViewCell и добавить UITextField в свой метод -(void)layoutSubviews.

Тогда вам нужно будет сослаться на этот CustomUITableViewCell и использовать его для загрузки вашего TableView.

Ссылка, которая поможет: Прочтите это!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...