Метод CollectionView didDeselectItemAt не работает

У меня проблема с методом didDeselectItemAt в представлении коллекции. Приложение: у меня есть коллекция с ячейками (см. скриншот). Пользователь может выделить только одну ячейку.

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

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

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    if collectionView == albumsCollectionView {
        guard let cell = collectionView.cellForItem(at: indexPath) as? AlbumCollectionCell else { return }
        cell.isCellSelected()
        presenter?.didChooseAlbum(with: indexPath.row) {
            self.picturesCollectionView.reloadData()
        }}

func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
    if collectionView == albumsCollectionView {
        guard let cell = collectionView.cellForItem(at: indexPath) as? AlbumCollectionCell else { return }
        cell.isCellDeselected()
    }
}

Метод CollectionView didDeselectItemAt не работает

cellForItem(at:) возвращает ноль, если ячейка не видна — это iOS15-. Какова ценность allowsMultipleSelection? Вместо этого вы можете вызвать isCellSelected внутри setSelected(_:animated:), чтобы изменить цвет.
Larme 16.05.2022 16:37

Проблема не в didDeselectItemAt ... проблема в том, что вы вызываете функцию в своей ячейке для отслеживания ее «выбранного состояния» вместо отслеживания выбранного состояния в вашем источнике данных.

DonMag 16.05.2022 16:45

привет, Ларме. Где я должен вызвать setSelected(_:animated:)?

Latynskii 16.05.2022 17:03

@Latynskiy - состояние «выбрано» — это только цвет фона?

DonMag 16.05.2022 18:15

@DonMag об этой коллекции - да, но ниже у меня есть еще одна коллекция с фотографиями из выбранного альбома

Latynskii 16.05.2022 18:47
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
59
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Как упоминалось в комментарии @DonMag, вы должны отслеживать выбранное состояние в своем источнике данных. Вот шаги.

Поскольку вы показываете некоторые данные в ячейке, вы можете создать для них модель.

struct Album {
    let name: String
    var isSelected: Bool
}

В AlbumCollectionCell добавьте переменную типа Album. И переопределите свойство isSelectedUICollectionViewCell

class AlbumCollectionCell: UICollectionViewCell {

    var album: Album? {
        didSet {
            guard let album = album else { return }

            if shoe.isSelected {
                self.backgroundColor = UIColor.orange
            }
            else {
                self.backgroundColor = UIColor.green
            }
            
           self.albumLabel.textColor = .white
           self.albumLabel.text = album.name
        }
    }
    
    override var isSelected: Bool {
        didSet {
            if self.isSelected {
                self.backgroundColor = UIColor.orange
            }
            else {
                self.backgroundColor = UIColor.green
            }
        }
    }
}

В UIViewController создайте массив типа Album, который содержит данные ячеек. Вы можете назначить данные методом viewDidLoad().

var albums = [Album]()

Установите allowsSelection свойство UICollectionView на true.

collectionView.allowsSelection = true

В методе cellForItem передайте данные Album в ячейку.

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AlbumCollectionCell", for: indexPath) as? AlbumCollectionCell {
        cell.album = albums[indexPath.item]
        return cell
    }
    return UICollectionViewCell()
}

Измените метод didSelectItem, как показано ниже.

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    albums[indexPath.item].isSelected = true
}

И переопределить метод didDeselectItemAt.

func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
    albums[indexPath.item].isSelected = false
}

спасибо за такой огромный ответ! не могли бы вы написать мне о "обувь". Что есть?

Latynskii 16.05.2022 18:54

Это ошибка. Я написал ответ, взяв код из одного из моих проектов. Я обновил ответ. Пожалуйста, посмотрите.

MBT 16.05.2022 19:02

@MBT Я думаю, что collectionView.indexPathsForSelectedItems?.first будет лучше, если можно выбрать только одну ячейку. Добавлять модель только для отслеживания IndexPath одной выбранной ячейки — плохая идея.

Elevo 17.05.2022 04:05

@MBT это работает, но я только что использовал isSelected и DidSet. Благодарю вас! А есть еще один вопрос. как сделать так, чтобы при открытии экрана с коллекцией уже выделялась определенная ячейка?

Latynskii 17.05.2022 09:03

@Latynskiy, вы можете выбрать элемент в методе viewDidLoad() после настройки collectionView, например let indexPath = IndexPath(item: 5, section: 0) collectionView.selectItem(at: indexPath, animated: false, scrollPosition: UICollectionView.ScrollPosition.centeredHorizontally)

MBT 17.05.2022 09:09

@MBT я пробовал это и, к сожалению, не работает, код: пусть indexPath = IndexPath (строка: 1, раздел: 0) альбомыCollectionView.selectItem (at: indexPath, анимированные: true, scrollPosition: .top)

Latynskii 17.05.2022 12:04

Не могли бы вы попробовать это в методе viewDidLayoutSubViews()? Надеюсь, это сработает.

MBT 17.05.2022 12:07

@MBT, извините, теперь это работа. Спасибо и хорошего дня!

Latynskii 17.05.2022 13:03

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

Однако, если вас не беспокоит постоянство, вы можете позволить представлению коллекции справиться с этим.

В классе вашей ячейки переопределите isSelected:

override var isSelected: Bool {
    didSet {
        contentView.backgroundColor = isSelected ? .systemOrange : .systemGreen
    }
}

Теперь вам не нужен код «внешнего вида ячейки» в didSelectItemAt или в didDeselectItemAt... представление коллекции будет управлять выбранным состоянием.

Вот быстрый, полный пример...

Пользовательский класс ячеек

class LabelCollectionViewCell: UICollectionViewCell {
    let label = UILabel()
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {
        label.textColor = .white
        label.textAlignment = .center
        label.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(label)
        let g = contentView.layoutMarginsGuide
        NSLayoutConstraint.activate([
            label.topAnchor.constraint(equalTo: g.topAnchor, constant: 4.0),
            label.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
            label.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
            label.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -4.0),
        ])
        contentView.backgroundColor = .systemGreen
        contentView.layer.cornerRadius = 12
    }
    override var isSelected: Bool {
        didSet {
            contentView.backgroundColor = isSelected ? .systemOrange : .systemGreen
        }
    }
}

Пример класса контроллера

class SelColViewVC: UIViewController {
    
    var albumsCollectionView: UICollectionView!
    
    let myData: [String] = [
        "Favorites",
        "Recents",
        "Recently Added",
        "Something Else",
        "Five",
        "Six",
        "Seven",
        "Eight",
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let fl = UICollectionViewFlowLayout()
        fl.scrollDirection = .horizontal
        fl.estimatedItemSize = CGSize(width: 120, height: 50)
        albumsCollectionView = UICollectionView(frame: .zero, collectionViewLayout: fl)
        albumsCollectionView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(albumsCollectionView)
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            albumsCollectionView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            albumsCollectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            albumsCollectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            albumsCollectionView.heightAnchor.constraint(equalToConstant: 80.0),
        ])
        
        albumsCollectionView.contentInset = UIEdgeInsets(top: 0.0, left: 8.0, bottom: 0.0, right: 8.0)
        albumsCollectionView.register(LabelCollectionViewCell.self, forCellWithReuseIdentifier: "cell")
        albumsCollectionView.dataSource = self
        albumsCollectionView.delegate = self
        
    }
    
}
extension SelColViewVC: UICollectionViewDataSource, UICollectionViewDelegate {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return myData.count
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let c = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! LabelCollectionViewCell
        c.label.text = myData[indexPath.item]
        return c
    }

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        if collectionView == albumsCollectionView {
            presenter?.didChooseAlbum(with: indexPath.item) {
                self.picturesCollectionView.reloadData()
            }}
        }
    }
}

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