Множественный выбор UITableView

Как я могу добавить UITableView в мое приложение на основе View, где пользователь будет использовать несколько ячеек, и он станет выбранным, точно так же, как и приложение "Новый будильник" Clock Clock с названием "Repeat" (Часы > Сигналы тревоги > + > Repeat), и как я могу получить все выделенные ячейки в массиве?

Ответ 1

В вашей реализации -tableView:didSelectRowAtIndexPath: вы должны установить свойство ячейки таблицы accessoryType в зависимости от его текущего значения (так что он будет включать и выключать с несколькими кранами). Например:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)path {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:path];

    if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
        cell.accessoryType = UITableViewCellAccessoryNone;
    } else {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    }
}

Вы можете либо сохранить массив выбранных состояний в дополнение к собственному состоянию типа аксессуаров ячеек, либо выполнить итерацию по ячейкам в представлении табличного представления для каждого одного состояния, чтобы считывать выбранные строки.

Ответ 2

Для множественного выбора добавьте строку ниже в viewDidLoad()

tableView.allowsMultipleSelection = true

Настройте каждый cell после удаления (или инициализации) его в tableView(_:cellForRowAt:)

let selectedIndexPaths = tableView.indexPathsForSelectedRows
let rowIsSelected = selectedIndexPaths != nil && selectedIndexPaths!.contains(indexPath)
cell.accessoryType = rowIsSelected ? .checkmark : .none
// cell.accessoryView.hidden = !rowIsSelected // if using a custom image

Обновление каждой ячейки при ее выборе/снятии выделения

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let cell = tableView.cellForRow(at: indexPath)!
    cell.accessoryType = .checkmark
    // cell.accessoryView.hidden = false // if using a custom image
}

override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
    let cell = tableView.cellForRow(at: indexPath)!
    cell.accessoryType = .none
    // cell.accessoryView.hidden = true  // if using a custom image
}

Когда вы закончите, получите массив всех выбранных строк

let selectedRows = tableView.indexPathsForSelectedRows

и получить выбранные данные, где dataArray сопоставляется с строками представления таблицы только с 1 секцией

let selectedData = selectedRows?.map { dataArray[$0.row].ID }

Ответ 3

@BrendanBreg реализация не сработала для меня. @RaphaelOliveira предоставила хорошее решение, но когда вы прокручиваете таблицу вниз, выбираются неправильные строки (потому что UITableView кэширует ячейки). Итак, я немного изменил решение Рафаэля:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
}

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
    cell.accessoryType = UITableViewCellAccessoryNone;
}

/*Here is modified part*/

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    /*
    ...
    Your implementation stays here
    we're just adding few lines to make sure
    that only correct rows will be selected
    */

    if([[tableView indexPathsForSelectedRows] containsObject:indexPath]) {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    } else {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }
}

Ответ 4

Просто подскажите в дополнение к большому ответу выше: подражать стилю Apple из приложения часов (если после выбора/снятия строки строки вытереть цвет выбора строки), добавьте это в didSelectRowAtIndexPath после условные:

[self.tableView deselectRowAtIndexPath:indexPath animated:YES];

Из руководства Apple TableMultiSelect.

Ответ 5

self.tableView.allowsMultipleSelection = YES;

Ответ 7

Я видел эту проблему со многими разработчиками. Из-за особенностей вида таблицы повторного использования ячейки он удаляет или случайно обнаруживает чеки. Я создал для этого рабочее решение. Клонировать/скачать код из DevelopmentSupportWorkspace и выполнить проект UITableViewTest оттуда.

Вот код для этого:

@interface CheckBoxTestTableViewController ()

@property (nonatomic, strong) NSArray *dataArray;
@property (nonatomic, strong) NSMutableDictionary *selectedIndexDictionary;

@end

@implementation CheckBoxTestTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // Uncomment the following line to preserve selection between presentations.
    // self.clearsSelectionOnViewWillAppear = NO;

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;

    //
    _dataArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ImageList" ofType:@"plist"]];
    _selectedIndexDictionary = [NSMutableDictionary dictionary];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return _dataArray.count;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"checkMarkCell" forIndexPath:indexPath];
    cell.accessoryType = UITableViewCellAccessoryNone;

    // Configure the cell...
    cell.textLabel.text = _dataArray[indexPath.row][@"text"];
    if (_selectedIndexDictionary[indexPath] != nil) cell.accessoryType = UITableViewCellAccessoryCheckmark;

    return cell;
}

- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
    if (_selectedIndexDictionary[indexPath] == nil) {
        [_selectedIndexDictionary setObject:_dataArray[indexPath.row] forKey:indexPath];
        [[tableView cellForRowAtIndexPath:indexPath] setAccessoryType:UITableViewCellAccessoryCheckmark];
    }else{
        [_selectedIndexDictionary removeObjectForKey:indexPath];
        [[tableView cellForRowAtIndexPath:indexPath] setAccessoryType:UITableViewCellAccessoryNone];
    }
//    [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
@end

Ответ 8

Сигналы будильника для повторного просмотра таблицы не являются множественным выбором. Подумайте об этом как об освещении флажков.

Когда ячейка выбрана, цвет шрифта и тип аксессуара изменяются, и выбор исчезает. Когда выбрана ячейка с проверкой, цвет шрифта и тип аксессуара меняются назад, а выбор исчезает.

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

В вашем методе cellForRowAtIndexPath: dataSource установите цвет текста и тип аксессуаров на основе вашей модели данных.

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

Ответ 9

Вы не можете отменить indexPath, потому что ячейки, которые ссылаются на изменения, как вы scoll. Поместите NSLog в cellForRowAtIndexPath, чтобы это увидеть. Вы можете выполнить check/uncheck в файле willSelectRowAtIndexPath или didSelectRowAtIndexPath. Это касается только начальной проверки или снятия флажка, хотя и будет отображаться как указано после прокрутки, потому что изменяется базовая ячейка для данного индекса.

Итак, решение, которое я нашел, состоит в том, чтобы иметь массив выбранных вещей с чем-то, что является специфичным для данной ячейки, и выполнить начальную проверку.

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];

  if (![selectedIndexes containsObject:cell.textLabel.text]){
    [selectedIndexes addObject:cell.textLabel.text];
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
  } else {
    [selectedIndexes removeObject:cell.textLabel.text];
    cell.accessoryType = UITableViewCellAccessoryNone;
  }

  return indexPath;
}

Вы также должны иметь логику в cellForRowAtIndexPath, чтобы убедиться, что нужный элемент проверен или нет, поскольку свитки просмотра:

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

  ...

  cell.accessoryType = UITableViewCellAccessoryNone;
  if ([selectedIndexes containsObject:cell.textLabel.text]) {
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
    [cell setSelected:YES animated:YES];
  }

  return cell;
}

Ответ 10

1 - разрешить множественный выбор и переключить выбранное состояние:

tableView.allowsMultipleSelection = true

2 - Собирайте или получите массив всех выбранных индексов, когда вы закончите:

let selected = tableView.indexPathsForSelectedRows

Примечание. Это не зависит от того, какие ячейки в данный момент отображаются на экране.

3 - Измените внешний вид ячейки в зависимости от выбранного состояния: переопределите этот метод в подклассе UITableViewCell. Если у вас нет подкласса, сделайте его.

// In UITableViewCell subclass
override func setSelected(selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)
    accessoryType = selected ? .Checkmark : .None
}