在 UICollectionViewController 中只选择一项
Posted
技术标签:
【中文标题】在 UICollectionViewController 中只选择一项【英文标题】:Select only one item in UICollectionViewController 【发布时间】:2017-05-26 15:47:48 【问题描述】:我已经实现了 UICollectionViewController 的一个子类,它可以水平滚动,我希望它一次只能选择一个项目。
当我更改当前屏幕上的选定项目时,它工作正常。但是,例如,如果我在集合的最开始选择一个项目,然后向右滚动并选择另一个项目,则仍然会选择第一个项目。
这是我的 CollectionView 的当前版本:
class GenresCollectionVC: UICollectionViewController
var selectedIndexPath: IndexPath?
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int
return 1
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return MockData.instance.genres.count
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: reuseIdentifier, for: indexPath) as! GenreCollectionViewCell
cell.genreNameLabel.text = MockData.instance.genres[indexPath.row]
if selectedIndexPath == indexPath
redraw(selectedCell: cell)
else
redraw(deselectedCell: cell)
return cell
// MARK: UICollectionViewDelegate
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
guard let cell = collectionView.cellForItem(at: indexPath) as? GenreCollectionViewCell else
return
redraw(selectedCell: cell)
selectedIndexPath = indexPath
override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath)
guard let cell = collectionView.cellForItem(at: indexPath) as? GenreCollectionViewCell else
return
redraw(deselectedCell: cell)
selectedIndexPath = nil
private func redraw(selectedCell cell: GenreCollectionViewCell
)
cell.layer.borderWidth = 1.0
cell.layer.cornerRadius = cell.bounds.height / 2
cell.layer.borderColor = UIColor.violetNeeoColor.cgColor
cell.genreNameLabel.textColor = UIColor.violetNeeoColor
private func redraw(deselectedCell cell: GenreCollectionViewCell)
cell.layer.borderWidth = 0.0
cell.layer.cornerRadius = 0.0
cell.genreNameLabel.textColor = UIColor.white
我做错了什么?
【问题讨论】:
【参考方案1】:在您的 GenreCollectionViewCell
类中,覆盖 isSelected
属性,即
override var isSelected: Bool
willSet
super.isSelected = newValue
if newValue
self.layer.borderWidth = 1.0
self.layer.cornerRadius = self.bounds.height / 2
self.layer.borderColor = UIColor.violetNeeoColor.cgColor
self.genreNameLabel.textColor = UIColor.violetNeeoColor
else
self.layer.borderWidth = 0.0
self.layer.cornerRadius = 0.0
self.genreNameLabel.textColor = UIColor.white
现在您无需在didSelectItemAt
delegate
方法中手动选择/取消选择单元格。 isSelected
属性会自动处理。
【讨论】:
酷,它帮助了我!但是可以解释为什么我们应该打电话给super.isSelected = newValue
?现在删除这行代码不会改变我的程序中的任何内容
如果你删除这一行,什么都没有改变意味着程序不工作?
不,当我使用您的代码时一切正常。即使我删除super.isSelected = newValue
行,一切仍然有效。所以问题是:“我为什么要写这行代码,它有什么变化?”
你其实不需要那行,我想@PGDev 忘了他是用 willSet 而不是 set,super.isSelected = newValue 会在隐式set
中自动调用【参考方案2】:
在这种情况下,保存 IndexPath
然后使用它作为比较的基础对我来说始终是最佳选择。
首先定义你的变量:
var _selectedIndexPath : IndexPath? = nil
然后在你的didSelectItemAtIndexPath
:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
if ((_selectedIndexPath) != nil)
if indexPath.compare(_selectedIndexPath!) == ComparisonResult.orderedSame
//if the user tap the same cell that was selected previously deselect it.
_selectedIndexPath = nil;
else
\ // save the currently selected indexPath
_selectedIndexPath = indexPath
else
// else, savee the indexpath for future reference if we don't have previous selected cell
_selectedIndexPath = indexPath;
// and now only reload only the visible cells
collectionView.reloadItems(at: collectionView.indexPathsForVisibleItems)
最后检查cellForItemAtIndexPath
中的选定单元格:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
// Demo implementation
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath as IndexPath)
if _selectedIndexPath == indexPath
//If the cell is selected
cell.isSelected = true
cell.backgroundColor = UIColor.red
else
// If the cell is not selected
cell.isSelected=false
cell.backgroundColor = UIColor.gray
return cell
【讨论】:
这是正确的答案,但 didSelectItem 有点不稳定。这是我的要点以及更好的版本:gist.github.com/PeeJWeeJ/414897cfeba33a8ea8edb51239a07612 @GetSwifty 你为什么要删除你的要点,PJ?【参考方案3】:选择新的indexPath
后手动取消选择单元格
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
guard let cell = collectionView.cellForItem(at: indexPath) as? GenreCollectionViewCell else
return
if selectedIndexPath != indexPath
collectionView(collectionView, didDeselectItemAt: selectedIndexPath)
redraw(selectedCell: cell)
selectedIndexPath = indexPath
显然,每点击一次单元格就会调用一次取消选择委托方法。
【讨论】:
【参考方案4】:private var selectedIndexPath: IndexPath? = nil
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "tagcell", for: indexPath) as! TagCell
cell.title = items[indexPath.item]
cell.isSelected = selectedIndexPath == indexPath
return cell
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
if selectedIndexPath != nil
if indexPath != selectedIndexPath
selectedIndexPath = indexPath
else
selectedIndexPath = indexPath
collectionView.reloadItems(at: collectionView.indexPathsForVisibleItems)
【讨论】:
以上是关于在 UICollectionViewController 中只选择一项的主要内容,如果未能解决你的问题,请参考以下文章
NOIP 2015 & SDOI 2016 Round1 & CTSC 2016 & SDOI2016 Round2游记