UICollectionView - didDeselectItemAtIndexPath не вызывается, если выбрана ячейка

Первое, что я делаю, это устанавливаю выбранную ячейку.

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    cell.selected = YES;
    return cell;
}

И ячейка удачно выбрана. Если пользователь касается выбранной ячейки, то эта ячейка должна быть отменена, и делегаты должны быть вызваны. Но этого никогда не случится.

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"%s", __PRETTY_FUNCTION__);
}

- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"%s", __PRETTY_FUNCTION__);
}

Я знаю, что делегатов не вызывают, если я устанавливаю выбор программно. Делегат и источник данных установлены.

Тем не менее, этот делегат вызывается:

- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"%s", __PRETTY_FUNCTION__);
    return YES;
}

Если я удалю cell.selected = YES чем все работает. Есть ли кто-нибудь, кто может мне объяснить это поведение?

8 ответов

Решение

Проблема в том, что ячейка выбрана, но UICollectionView ничего не знает об этом. Дополнительный звонок для UICollectionView решает проблему:

[collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; 

Полный код:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    cell.selected = YES;
    [collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];
    return cell;
}

Я держу вопрос, чтобы помочь кому-то, кто может столкнуться с той же проблемой.

Я думаю, что решение @zeiteisen - это обходной путь. Реальная вещь лежит в режиме выбора. Там нет ничего, чтобы установить в свойстве cell.selected,

Есть два свойства UICollectionView название allowsMultipleSelection а также allowsSelection,

allowsMultipleSelection является NO по умолчанию и allowsSelection является YES,

Если вы хотите выбрать только одну ячейку, то код @ инициализация выглядит так

yourCollectionView.allowsMultipleSelection = NO;
yourCollectionView.allowsSelection = YES; //this is set by default

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

Если вы хотите выбрать несколько ячеек, то код @ инициализация должен выглядеть следующим образом:

yourCollectionView.allowsMultipleSelection = YES;
yourCollectionView.allowsSelection = YES; //this is set by default

Тогда настройки позволят выбрать несколько ячеек. Эти настройки позволяют ни один или несколько UICollectionViewCell быть выбранным. Номер выбранной ячейки будет

0 <= number_selected_cell <= total_number_of_cell

Это политика выбора нескольких ячеек.

Если вы намереваетесь использовать что-либо из вышеперечисленного, вы можете использовать яблочный API без дополнительной головной боли, в противном случае вам нужно найти способ проникнуть в вашу цель, используя свой собственный код или яблочный API.

Я была такая же проблема. Я предварительно выбрал ячейки при загрузке таблицы, но мне пришлось дважды нажать ячейку, чтобы отменить ее выбор. Ответ @zeiteisen помог мне. Тем не менее, я работаю в Swift 3, поэтому я решил дать ответ в Swift.

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell

    if /*Test for Selection*/ {
        cell.isSelected = true
        collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .left)
    }        
    return cell
}

У меня была такая же проблема, если вы исследуете дальше, вы увидите, что не только дидиселект не вызывается, но и само прикосновение не воспринимается клеткой. Для меня было решено разрешить асинхронное выполнение выбора и установить selecItem вручную. Внутри cellForItemAt.

       DispatchQueue.main.async {
            cell.isSelected = true
            collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .init())
        }

Используйте.init() и false для анимации, если вы не хотите видеть движения. Если вы не заключите это в блок async, вы увидите, как пользовательский интерфейс прыгает при прокрутке.

Добавьте эту строку в ваш метод didSelectItemAtIndexPath

[collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:nil]

В моем случае я хочу изменить фон кнопки, другими словами фон ячейки в представлении коллекции:

      class CustomCVCell: UICollectionViewCell {

override var isSelected: Bool {
        didSet {
            grayBackgroundViewWithImage.image =
                isSelected ? UIImage(named: "") : UIImage()
        }
    }

В основном классе, где хранится представление коллекции, создайте эту переменную:

      class CustomViewController: UIViewController {

///save the indexPath of last selected cell
private var lastSelectedIndexPath: IndexPath? }

В viewDidLoad () установите это значение в false:

      customCollectionView.allowsMultipleSelection = false

Дальнейший код в источнике данных. В моем случае должна быть выбрана первая ячейка:

      func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CustomCVCell.cellID(),
                                                  for: indexPath) as! CustomCVCell
    
    if indexPath.row == 0 {
        lastSelectedIndexPath = indexPath
        cell.isSelected = true
    }
    
    //update last select state from lastSelectedIndexPath
    cell.isSelected = (lastSelectedIndexPath == indexPath)
    
    return cell
}

Дальнейший код в делегате:

      ///UICollectionViewDelegate
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    guard lastSelectedIndexPath != indexPath else { return }

         if let index = lastSelectedIndexPath {
            let cell = collectionView.cellForItem(at: index) as! CustomCVCell
            cell.isSelected = false
          }

          let cell = collectionView.cellForItem(at: indexPath) as! CustomCVCell
          cell.isSelected = true
    lastSelectedIndexPath = indexPath
}

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

func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool

что касается

func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool

И когда ячейка была выбрана, должен HighlightItemAt возвращал false - и это не позволило мне отменить выбор этой ячейки.

Надеюсь, это сэкономит кому-то время:)

Перезагрузка камеры была решением для меня. Что мне нужно, так это кратковременное изменение изображения в камере. Я назначаю изображение для imageView в cellForItemAtIndexPath, и когда пользователь касается ячейки, я меняю изображение и запускаю mycode, затем перезагружаю ячейку.

 func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
        let cell = collectionView.cellForItemAtIndexPath(indexPath) as! MenuCollectionViewCell
        cell.backgroundImageView.image = UIImage(named: "selected")
        // handle tap events
        collectionView.reloadItemsAtIndexPaths([indexPath])
    }

Надеюсь, это поможет кому-то.

Другие вопросы по тегам