如何使用 UICollectionView 的 selectItem 在 collectionView 单元格上绘图?

Posted

技术标签:

【中文标题】如何使用 UICollectionView 的 selectItem 在 collectionView 单元格上绘图?【英文标题】:How to draw on collectionView cells using UICollectionView's selectItem? 【发布时间】:2018-10-02 18:08:17 【问题描述】:

我添加了我在这里工作的 repo:

https://github.com/AlexMarshall12/singleDayTimeline/tree/master/singleDayTimeline

基本上我有 900 个 collectionView 单元格(带有自定义 XIB 布局)。

    let cellIdentifier = "DayCollectionViewCell"
class ViewController: UIViewController, UICollectionViewDataSource,UICollectionViewDelegate 

    @IBOutlet weak var button: UIButton!
    var dates = [Date?]()
    var startDate: Date?
    @IBOutlet weak var daysCollectionView: UICollectionView!
    override func viewDidLoad() 
        super.viewDidLoad()
        daysCollectionView.register(UINib.init(nibName: "DayCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: cellIdentifier)

        let allDates = Helper.generateRandomDate(daysBack: 900, numberOf: 10)
        self.dates = allDates.sorted(by: 
            $0!.compare($1!) == .orderedAscending
        )
        startDate = self.dates.first! ?? Date()

        daysCollectionView.delegate = self
        daysCollectionView.dataSource = self
        // Do any additional setup after loading the view, typically from a nib.
    

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
        return 900
    

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        let cell = daysCollectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! DayCollectionViewCell

        let cellDate = Calendar.current.date(byAdding: .day, value: indexPath.item, to: self.startDate!)

        if Calendar.current.component(.day, from: cellDate!) == 15 
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "MMM"
            let monthString = dateFormatter.string(from: cellDate!)
            cell.drawMonth(month: monthString)
        
        if Calendar.current.component(.day, from: cellDate!) == 1 && Calendar.current.component(.month, from: cellDate!) == 1 
            print("drawYEAR")
            cell.drawYear(year:Calendar.current.component(.year, from: cellDate!))
        
        if self.dates.contains(where:  Calendar.current.isDate(cellDate!, inSameDayAs: $0!) ) 
            print("same")
            cell.backgroundColor = UIColor.red
         else 
            print("not me")
            //cell.backgroundColor = UIColor.lightGray
        
        return cell
    

//    func collectionView(_ collectionView: UICollectionView,
//                        layout collectionViewLayout: UICollectionViewLayout,
//                        sizeForItemAt indexPath: IndexPath) -> CGSize 
//        return CGSize(width: 2, height: daysCollectionView.bounds.size.height/2 )
//    
    @IBAction func buttonPressed(_ sender: Any) 

        let randomIndex = Int(arc4random_uniform(UInt32(self.dates.count)))
        let randomDate = self.dates[randomIndex]
        let daysFrom = randomDate?.days(from: self.startDate!)
        let indexPath = IndexPath(row: daysFrom!, section: 0)
//        if let cell = daysCollectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as DayCollectionViewCell? 
//            print("found it")
//         else 
//            print("didn't find it")
//        
        daysCollectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredHorizontally)
        daysCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
    
    

然后这里是单元格:

   class DayCollectionViewCell: UICollectionViewCell 

    @IBOutlet weak var arrowImage: UIImageView!

    override var isSelected: Bool
        didSet
            arrowImage.isHidden = !isSelected
        
    
    override func awakeFromNib() 
        super.awakeFromNib()
        arrowImage.isHidden = true
    

    override func prepareForReuse() 
        self.backgroundColor = UIColor.clear
    

    func drawMonth(month: String)

    
    func drawYear(year: Int)

    


看起来像这样:

因此计划是,当按下该按钮时,您可以在@IBAction func buttonPressed 中看到选择并滚动到随机日期,然后在 collectionView 中选择该单元格。然后在 override var isSelected 函数中使用arrowImage.isHidden = !isSelected 绘制单元格的箭头。

目前,这几乎可以完美运行。当随机选择的新索引距离当前索引足够远时,将在选定单元格下重新绘制箭头。我的理论是,如果索引差异足够大,则下一个单元格尚未加载/出列,因此永远不会调用 isSelected。但是我仍然不确定为什么它不能正常工作

【问题讨论】:

【参考方案1】:

1- 添加一个 reloadCell 函数来更改单元格的 ui。然后你应该从awakeFromNib函数中删除override var isSelectedarrowImage.isHidden = true

func reloadCell(_ isSelected:Bool)
   arrowImage.isHidden = !isSelected

2- 你应该在ViewController.swiftprivate var selectedIndexPath: IndexPath? 上定义一个变量,然后你应该添加这个代码来检查箭头是否隐藏。

 if let selectedRow = selectedIndexPath 
     cell.reloadCell(selectedRow == indexPath)
  else 
     cell.reloadCell(false)
  

3- 如果您像下面这样更改按钮操作功能,它将起作用。

@IBAction func buttonPressed(_ sender: Any) 

    let randomIndex = Int(arc4random_uniform(UInt32(self.dates.count)))
    let randomDate = self.dates[randomIndex]
    let daysFrom = randomDate?.days(from: self.startDate!)
    let indexPath = IndexPath(row: daysFrom!, section: 0)
    self.selectedIndexPath = indexPath;

    daysCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
    daysCollectionView.reloadData()

所有代码都在这里。

ViewController.swift

import UIKit
let cellIdentifier = "DayCollectionViewCell"
class ViewController: UIViewController, UICollectionViewDataSource,UICollectionViewDelegate 

@IBOutlet weak var button: UIButton!
var dates = [Date?]()
var startDate: Date?
private var selectedIndexPath: IndexPath?

@IBOutlet weak var daysCollectionView: UICollectionView!

override func viewDidLoad() 
    super.viewDidLoad()
    daysCollectionView.register(UINib.init(nibName: "DayCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: cellIdentifier)

    let allDates = Helper.generateRandomDate(daysBack: 900, numberOf: 10)
    self.dates = allDates.sorted(by: 
        $0!.compare($1!) == .orderedAscending
    )
    startDate = self.dates.first! ?? Date()

    daysCollectionView.delegate = self
    daysCollectionView.dataSource = self


func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    return 900


func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = daysCollectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! DayCollectionViewCell

    let cellDate = Calendar.current.date(byAdding: .day, value: indexPath.item, to: self.startDate!)

    if let selectedRow = selectedIndexPath 
        cell.reloadCell(selectedRow == indexPath)
     else 
        cell.reloadCell(false)
    

    if Calendar.current.component(.day, from: cellDate!) == 15 
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "MMM"
        let monthString = dateFormatter.string(from: cellDate!)
        cell.drawMonth(month: monthString)
    
    if Calendar.current.component(.day, from: cellDate!) == 1 && Calendar.current.component(.month, from: cellDate!) == 1 
        print("drawYEAR")
        cell.drawYear(year:Calendar.current.component(.year, from: cellDate!))
    
    if self.dates.contains(where:  Calendar.current.isDate(cellDate!, inSameDayAs: $0!) ) 
        print("same")
        cell.backgroundColor = UIColor.red
     else 
        print("not me")
        //cell.backgroundColor = UIColor.lightGray
    
    return cell


@IBAction func buttonPressed(_ sender: Any) 

    let randomIndex = Int(arc4random_uniform(UInt32(self.dates.count)))
    let randomDate = self.dates[randomIndex]
    let daysFrom = randomDate?.days(from: self.startDate!)
    let indexPath = IndexPath(row: daysFrom!, section: 0)
    self.selectedIndexPath = indexPath;

    //daysCollectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredHorizontally)
    daysCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
    daysCollectionView.reloadData()



DayCollectionViewCell.swift

import UIKit

class DayCollectionViewCell: UICollectionViewCell 

@IBOutlet weak var arrowImage: UIImageView!

override func awakeFromNib() 
    super.awakeFromNib()


override func prepareForReuse() 
    self.backgroundColor = UIColor.clear


func drawMonth(month: String)


func drawYear(year: Int)



func reloadCell(_ isSelected:Bool)
    arrowImage.isHidden = !isSelected



【讨论】:

以上是关于如何使用 UICollectionView 的 selectItem 在 collectionView 单元格上绘图?的主要内容,如果未能解决你的问题,请参考以下文章

如何避免带有缺口的设备上 UICollectionView 搜索栏下方的空白?

-[UICollectionView _endItemAnimations] 崩溃中的断言失败

-[UICollectionView _endItemAnimations] 中的 UICollectionView 断言失败

使用附加控件注释 UICollectionView 选择的设计

UICollectionView 的 deleteSections() 动画

向 UICollectionview 添加一个顶部标题,该标题已经包含带有标题的部分