【问题标题】:Select only one item in UICollectionViewController在 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
    }
}

我做错了什么?

【问题讨论】:

    标签: ios swift uicollectionview


    【解决方案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
        }
    

    【讨论】:

    【解决方案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)
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-12-08
        • 2019-01-18
        • 2013-02-24
        • 1970-01-01
        • 2019-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多