微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在 CollectionVIew 中重新加载数据时按钮闪烁

如何解决在 CollectionVIew 中重新加载数据时按钮闪烁

我正在通过集合视图中的按钮点击更新类别图像。我有一个 collectionView,当点击按钮并且图像发生变化时,它具有淡入动画。我也尝试使用 reloadItem,但它仍然闪烁。

我发现问题是因为我在单元格更新内部调用了reloadData,但我找不到按下按钮时如何正确执行。

视图控制器


import UIKit
import CoreData


class WordsVC: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource {
  
    
  
    @IBOutlet weak var wordsCategoryCollection: UICollectionView!
    

    override func viewDidLoad() {
        super.viewDidLoad()

        wordsCategoryCollection.delegate = self
        wordsCategoryCollection.dataSource = self
        wordsCategoryCollection.isScrollEnabled = false
        wordsCategoryCollection.backgroundColor = nil
        
        roundCounter = 1
    }

    
    func collectionView(_ collectionView: UICollectionView,numberOfItemsInSection section: Int) -> Int {
        return words.count
    }
    
    func collectionView(_ collectionView: UICollectionView,cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "WordsCategoryCell",for: indexPath) as? WordsCategoryCell {
            cell.backgroundColor = UIColor.clear
            
            
            cell.wordsBtn.tag = indexPath.row
            cell.wordsBtn.addTarget(self,action: #selector(changeCategoryStatus),for: .touchUpInside)
            cell.updateCell(word: words[indexPath.row])
            return cell
        }
        return UICollectionViewCell()
    }
    
    @objc func changeCategoryStatus(_ sender: UIButton!) {
        debugPrint(sender.tag)
        if words[sender.tag].categoryIsEnable == true {
                words[sender.tag].categoryIsEnable = false
                words[sender.tag].categoryImageName.removeLast()
                words[sender.tag].categoryImageName.append("F")
              } else {
                words[sender.tag].categoryIsEnable = true
                words[sender.tag].categoryImageName.removeLast()
                words[sender.tag].categoryImageName.append("T")
              }
        
        debugPrint("\(words[0].categoryImageName) - \(words[0].categoryIsEnable)")
        debugPrint("\(words[1].categoryImageName) - \(words[1].categoryIsEnable)")
        debugPrint("\(words[2].categoryImageName) - \(words[2].categoryIsEnable)")
        wordsCategoryCollection.reloadData()
        //wordsCategoryCollection.reloadItems(at: [IndexPath(row: sender.tag,section: 0)])
    }
    
    override func prepare(for segue: UIStoryboardSegue,sender: Any?) {
        getWordsForTheGame()
    }
    
    @IBAction func playBtn(_ sender: Any) {
        performSegue(withIdentifier: "MainVC",sender: nil)
    }
    
}


细胞

class WordsCategoryCell: UICollectionViewCell {

    @IBOutlet weak var wordsBtn: UIButton!
    
    func updateCell(word: word) {
        let image = UIImage(named: word.categoryImageName)
        wordsBtn.setTitle(nil,for: .normal)
        wordsBtn.setimage(image,for: .normal)
    }
    
}

GIF

解决方法

我建议您不要将 reloadData() 用于您想要实现的目标。相反,我会推荐这个:

func collectionView(_ collectionView: UICollectionView,shouldSelectItemAt indexPath: IndexPath) -> Bool {
        collectionView.indexPathsForSelectedItems?.forEach({ collectionView.deselectItem(at: $0,animated: false) })
        //this function will make it so only one cell can be selected at a time
        //don't add this if you want multiple cells to be selected at the same time.
        return true
    }
    
    func collectionView(_ collectionView: UICollectionView,didDeselectItemAt indexPath: IndexPath) {
        self.checkNumberOfAnswers()
        let cell = collectionView.cellForItem(at: indexPath) as! WordsCategoryCell
        //animate the cell that is getting unselected here
    }
    
    func collectionView(_ collectionView: UICollectionView,didSelectItemAt indexPath: IndexPath) {
        self.checkNumberOfAnswers()
        let cell = collectionView.cellForItem(at: indexPath) as! WordsCategoryCell
        //animate the cell that is getting selected here
    }

您可以按照您想要的方式为您选择的单元格设置动画,甚至可以更改其数据,因为您可以访问该单元格,对于未选中的单元格也是如此。

只需覆盖单元格内的 isSelected 并将其设置为在选择单元格和未选择单元格时执行您想要的操作。

public override var isSelected: Bool {
        didSet {
            switch self.isSelected {
            case true:
                //do stuff when selected
            case false:
                //do stuff when unselected
            }
        }
    }

编辑:

reloadData() 使用 func collectionView(_ collectionView: UICollectionView,cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 中的所有代码重新创建当前加载的 所有 单元格(因为您正在出列,所以在视图中),对于这个特定用例来说效率不高因为它再次运行额外的代码,如果它已经在视图中并且它是使用您的配置加载的,那么不需要再次设置背景颜色以清除每个单元格的按钮标签等...。您看到其他细胞发生的小动画可能是细胞在您眼前再次创建的副作用。 与其更改 words 数组中的数据,不如更改已创建的单元格中的数据。

reloadData() 更好地用于实际重新加载新数据,例如,当您从服务器发出请求并获得一个新数组来更新您的单元格时,您可以在其上放置一个 didSet数组,当 didSet 运行时,它将 reloadData,使用实际的新信息/数据重新创建单元格。

至于如何使用按钮,首先我建议您在单元格中添加 @objc 方法以提高可读性,因为这是您的按钮所在的位置,并使用委托在您的 ViewController 中获得通知。

其次,您需要 indexPath 来访问单元格,而不是 indexPath.row,因此在您的单元格内创建一个名为 indexPath 的可选变量,类型为 IndexPath 并将其设置为 indexPath正在创建单元格。

cell.indexPath = indexPath

现在,在您的单元格内创建一个委托,该委托具有在您按下按钮时传递 indexPath 的函数,例如:

protocol categoryCellDelegate {
    func buttonPressed(index: IndexPath)       
}

通过遵守协议以及在 VC 内部调用函数时获取 VC 内部的 indexPath:

func buttonPressed(index: IndexPath) {
      let cell = wordsCategoryCollection.cellForItem(at: index) as! WordsCategoryCell
    //now do anything your want with this cell like change image,color,etc...
}

为什么我推荐使用 CollectionView 的 didSelectItemAt 主要是因为它已经为您提供了所有这些和更多。

直到一个月前我一直使用按钮来做这件事(我已经编写 Swift 8 个月了),直到我想创建一个测验系统,一次只能选择给定部分中的一个单元格,确保取消选择前一个单元格并选择新单元格所涉及的头痛是不值得的。所以我使用了 Collections View 自己的选择方法,看到它是多么容易,它给了我我想要的一切,并开始更多地使用它。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。