Сохранение статусов выбора UITableViewCell [разрешено]

Я пробовал буквально все и все, что мог придумать. Может ли кто-нибудь понять, почему UITableViewCell не перезагружать галочки после выбора, прокрутки, а затем прокрутки назад?

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    #define CHECK_NULL_STRING(str) ([str isKindOfClass:[NSNull class]] || !str)[email protected]"":str

    static NSString *CellIdentifier = @"inviteCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    cell.accessoryType = UITableViewCellAccessoryCheckmark;
    cell.textLabel.highlightedTextColor = [UIColor colorWithHexString:@"#669900"];
    cell.selectionStyle = UITableViewCellSelectionStyleGray;
    cell.backgroundColor = [UIColor blackColor];
    cell.textLabel.textColor = [UIColor whiteColor];
    [[UITableViewCell appearance] setTintColor:[UIColor colorWithHexString:@"#669900"]];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] init];
    }

    if (cell == nil) {cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; }

    BOOL isSearching = tableView != self.tableView;
    NSArray *arrayToUse = (isSearching ? searchResults : contactsObjects);
    id p = arrayToUse[indexPath.row];

    NSString *fName = (__bridge_transfer NSString *)(ABRecordCopyValue((__bridge ABRecordRef)(p), kABPersonSortByFirstName));
    NSString *lName = (__bridge_transfer NSString *)(ABRecordCopyValue((__bridge ABRecordRef)(p), kABPersonSortByLastName));
    cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", CHECK_NULL_STRING(fName), CHECK_NULL_STRING(lName)];

    BOOL showCheckmark = [[stateArray objectAtIndex:indexPath.row] boolValue];
    if (showCheckmark == YES)
    {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
        NSLog(@"It hit showCheckmark = YES, and stateArray is %@",stateArray[indexPath.row]);
    }
    else
    {
        cell.accessoryType = UITableViewCellAccessoryNone;
        NSLog(@"It hit showCheckmark = NO, and stateArray is %@",stateArray[indexPath.row]);
    }

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
{
    id object = contactsObjects[indexPath.row];
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    if (cell.accessoryType == UITableViewCellAccessoryNone)
    {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
        [stateArray insertObject:[NSNumber numberWithBool:YES] atIndex:indexPath.row];
        [selectedObjects addObject:object];
    }
    else
    {
        cell.accessoryType = UITableViewCellAccessoryNone;
        [stateArray insertObject:[NSNumber numberWithBool:NO] atIndex:indexPath.row];
        [selectedObjects removeObject:object];
    }

    //slow-motion selection animation.
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}

Ответы

Ответ 1

Проблема выбора соты не была решена даже тогда, когда insertObject был заменен replaceWithObject, однако, не следует тратить время настройки BOOL объекты с NSInteger внутри NSMutableArray. Вместо этого для памяти выбора ячейки следует использовать NSDictionary следующим образом:

@property (nonatomic, strong) NSMutableDictionary * selectedRowCollection;

- (void)viewDidLoad{

    self.selectedRowCollection = [[NSMutableDictionary alloc] init];
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
{    
    id object = contactsObjects[indexPath.row];
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    if (cell.accessoryType == UITableViewCellAccessoryNone)
    {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
        [self.selectedRowCollection setObject:@"1" forKey:[NSString stringWithFormat:@"%d",indexPath.row]]; 
    }
    else
    {
        cell.accessoryType = UITableViewCellAccessoryNone;
        [self.selectedRowCollection removeObjectForKey:[NSString stringWithFormat:@"%d",indexPath.row]];
    }

    //slow-motion selection animation.
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}

   - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    BOOL showCheckmark =  [[self.selectedRowCollection valueForKey:[NSString stringWithFormat:@"%d",indexPath.row]] boolValue];

    if (showCheckmark == YES)
    {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;

    }
    else
    {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }
}

Если этот ответ помог вам вообще, не забудьте решить этот вопрос вверх! Спасибо!

Ответ 2

Вы пропустили ! (обратный оператор) в следующей строке, что означает, что состояние всегда будет одинаковым.

[stateArray replaceObjectAtIndex:indexPath.row withObject:[NSNumber numberWithBool:[[stateArray objectAtIndex:indexPath.row] boolValue]]];

Это должно быть

[stateArray replaceObjectAtIndex:indexPath.row withObject:[NSNumber numberWithBool:![[stateArray objectAtIndex:indexPath.row] boolValue]]];

Изменить --- Я переработал оба метода, потому что это можно сделать с гораздо меньшим количеством кода, и это полностью упростит ваши методы.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"inviteCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    BOOL isSearching = tableView != self.tableView;
    NSArray *arrayToUse = (isSearching ? searchResults : contactsObjects);
    id p = arrayToUse[indexPath.row];

    NSString *fName = (__bridge_transfer NSString *)(ABRecordCopyValue((__bridge ABRecordRef)(p), kABPersonSortByFirstName));
    NSString *lName = (__bridge_transfer NSString *)(ABRecordCopyValue((__bridge ABRecordRef)(p), kABPersonSortByLastName));
    cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", CHECK_NULL_STRING(fName), CHECK_NULL_STRING(lName)];

    BOOL showCheckmark = [stateArray[indexPath.row] boolValue];
    if (showCheckmark == YES) {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    }
    else {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }
    return cell;
}

- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    id object = contactsObjects[indexPath.row];
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if (cell.accessoryType == UITableViewCellAccessoryNone) {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
        [selectedObjects addObject:object];
    }
    else {
        cell.accessoryType = UITableViewCellAccessoryNone;
        [selectedObjects removeObject:object];
    }
    stateArray[indexPath.row] = @(cell.accessoryType == UITableViewCellAccessoryCheckmark);
}

Ответ 3

Я предлагаю более объектно-ориентированный подход. Это гарантирует, что ваш код будет гибким и будет отображаться правильно все время.

Для каждого элемента, который вы хотите отобразить в своей таблице, укажите соответствующий объект. Вы упомянули, что вы показываете контакты, поэтому предположим, что ваш объект называется "Контакт":

//Contact.h

@interface Contact : NSObject

@property BOOL selected;
@property NSString *name;

@end

//Contact.m
#import Contact.h
@implementation Contact

+ (id) contactWithName:(NSString*)name {
    Contact *nContact = [Contact new];
    nContact.name = name;
    nContact.selected = NO;
    return nContact;
}
@end

Затем просто сделайте свое представление таким, как это:

//ContactView.m

@interface ContactView()

@property NSMutableArray *contacts;

@end

@implementation ContactView
@synthesize contacts;

- (void) viewDidLoad {
    [super viewDidLoad];
    //get your contact list here. When creating contacts, be sure to assign their selected and their name as you require.
    contacts = @[[Contact contactWithName:@"John"], [Contact contactWithName:@"Jane"]];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellID = @"inviteCell";
    UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:cellID];
    if (cell == nil) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];

    Contact *cellContact = [contacts objectAtIndex:indexPath.row];
    cell.textLabel.text = cellContact.name;
    cell.accessoryType = cellContact.selected == YES ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    Contact *cellContact = [contacts objectAtIndex:indexPath.row];
    cellContact.selected = !cellContact.selected;
    [contacts replaceObjectAtIndex:indexPath.row withObject:cellContact];
    [tableView reloadData]; //to refresh without animation
    //[tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [tableView numberOfSections])] withRowAnimation:UITableViewRowAnimationTop]; //to refresh with animation
}

@end

И стрелы, простые в использовании таблицы, которые всегда выглядят правильно, стоят в очереди правильно и ориентированы на объекты для более легкого обслуживания позже.

Ответ 4

Вы не делаете этого UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:cellID]; if (cell == nil) {cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID]; } Выделение необходимо, если ячейка равна нулю. Или это вызывает проблемы при прокрутке.