У меня проблема с методом 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()
}
}
Проблема не в didDeselectItemAt
... проблема в том, что вы вызываете функцию в своей ячейке для отслеживания ее «выбранного состояния» вместо отслеживания выбранного состояния в вашем источнике данных.
привет, Ларме. Где я должен вызвать setSelected(_:animated:)?
@Latynskiy - состояние «выбрано» — это только цвет фона?
@DonMag об этой коллекции - да, но ниже у меня есть еще одна коллекция с фотографиями из выбранного альбома
Как упоминалось в комментарии @DonMag, вы должны отслеживать выбранное состояние в своем источнике данных. Вот шаги.
Поскольку вы показываете некоторые данные в ячейке, вы можете создать для них модель.
struct Album {
let name: String
var isSelected: Bool
}
В AlbumCollectionCell
добавьте переменную типа Album
. И переопределите свойство isSelected
UICollectionViewCell
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
}
спасибо за такой огромный ответ! не могли бы вы написать мне о "обувь". Что есть?
Это ошибка. Я написал ответ, взяв код из одного из моих проектов. Я обновил ответ. Пожалуйста, посмотрите.
@MBT Я думаю, что collectionView.indexPathsForSelectedItems?.first
будет лучше, если можно выбрать только одну ячейку. Добавлять модель только для отслеживания IndexPath одной выбранной ячейки — плохая идея.
@MBT это работает, но я только что использовал isSelected и DidSet. Благодарю вас! А есть еще один вопрос. как сделать так, чтобы при открытии экрана с коллекцией уже выделялась определенная ячейка?
@Latynskiy, вы можете выбрать элемент в методе viewDidLoad()
после настройки collectionView, например let indexPath = IndexPath(item: 5, section: 0) collectionView.selectItem(at: indexPath, animated: false, scrollPosition: UICollectionView.ScrollPosition.centeredHorizontally)
@MBT я пробовал это и, к сожалению, не работает, код: пусть indexPath = IndexPath (строка: 1, раздел: 0) альбомыCollectionView.selectItem (at: indexPath, анимированные: true, scrollPosition: .top)
Не могли бы вы попробовать это в методе viewDidLayoutSubViews()
? Надеюсь, это сработает.
@MBT, извините, теперь это работа. Спасибо и хорошего дня!
Если вы пытаетесь выбрать сопротивляться для выбора, то есть если вы хотите сохранить его и восстановить последний выбор между использованием приложения, или если вы уходите и хотите восстановить его при повторном переходе к этому контроллеру, вам нужно чтобы отслеживать его с помощью вашего источника данных.
Однако, если вас не беспокоит постоянство, вы можете позволить представлению коллекции справиться с этим.
В классе вашей ячейки переопределите 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()
}}
}
}
}
cellForItem(at:)
возвращает ноль, если ячейка не видна — это iOS15-. Какова ценностьallowsMultipleSelection
? Вместо этого вы можете вызватьisCellSelected
внутриsetSelected(_:animated:)
, чтобы изменить цвет.