如何在willDisplayCell回调中重新加载collectionView的单个单元格?

时间:2018-10-11 20:22:29

标签: ios swift uicollectionview

我有一个collectionView,我正在整理一个时间表。在水平collectionView中有许多瘦细胞-每个细胞代表1天。这是项目代码:https://github.com/AlexMarshall12/singleDayTimeline.git

这是viewController:

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

    @IBOutlet weak var button: UIButton!
    var dates = [Date?]()
    var startDate: Date?
    var endDate: 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
        })
        self.startDate = Calendar.current.startOfDay(for: dates.first as! Date)

        self.endDate = dates.last!
        self.dates = Array(dates.prefix(upTo: 1))
        daysCollectionView.delegate = self
        daysCollectionView.dataSource = self
    }

    var onceOnly = false

    internal func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        if !onceOnly {
            //let lastDateIndexPath = IndexPath(row: dates.count - 1,section: 0)
            let lastDate = dates.last
            let lastDayIndex = lastDate!?.interval(ofComponent: .day, fromDate: startDate!)
            let lastDayCellIndexPath = IndexPath(row: lastDayIndex!, section: 0)
            self.daysCollectionView.scrollToItem(at: lastDayCellIndexPath, at: .left, animated: false)
            self.selectedIndexPath = lastDayCellIndexPath
            self.daysCollectionView.reloadItems(at: [lastDayCellIndexPath])

            onceOnly = true
        }
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        let days = self.endDate!.days(from: self.startDate!)
        if days <= 150 {
            return 150
        } else {
            print(days,"days")
            return days + 1
        }
    }

    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
        }
        //cell.backgroundColor = UIColor.blue
        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()
    }

}

请注意,当调用buttonPressed函数时,“ selectedIndexPath”将更改。然后下一次调用cellForItemAt时,它会执行cell.reloadCell,如果indexPath是这个selectedIndexPath,它将取消隐藏arrowImageView(见下文)

class DayCollectionViewCell: UICollectionViewCell {

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

}

这在按下按钮时有效,但是我试图使其在willDisplayCell回调中起作用。您可以看到,在此回调中,我基本上只是获取最新(最新)日期并滚动到该日期,然后将SelectedIndexPath设置为该indexPath。现在,我需要一种使箭头在该单元格上绘制的方法。

这是现在的样子: enter image description here

如您所见,直到滚动时,它才显示箭头。我认为正在发生的事情是,当您滚动调用足够的cellForItemAt时,我们就知道了。但是,如何使willDisplayCell成功完成相同的操作,从而从初始启动时加载箭头?

2 个答案:

答案 0 :(得分:2)

在您的 viewDidLoad 中,您似乎正在初始化日期 startDate endDate

因此,在设置完这些变量后,您可以使用可用数据设置 selectedIndexPath 。然后,调用 cellForItem 方法时,其中的代码将自动处理箭头的可见性。

在初始化三个变量之后,立即将代码放置在 viewDidLoad 中。

if let startDate = startDate,
    let lastDayIndex = dates?.last?.interval(ofComponent: .day, fromDate: startDate) {
    selectedIndexPath = IndexPath(row: lastDayIndex, section: 0)
}

您无需在 willDisplayCell 中进行处理,也无需使用 onceOnly 布尔值。

答案 1 :(得分:1)

我添加一个新答案是因为我注意到其他答案的一些不完整细节。

在viewDidLoad中,有一个奇怪的代码:

 self.dates = Array(dates.prefix(upTo: 1))

我认为这是一个测试代码。因此结果将总共只有一个小节。不管是什么原因,都应该在该代码之后添加随机或最后一个随机代码。

    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)

请注意,我评论了最后一个。如果只有一个条,就足够了,但是如果有多个,则应该将scrollToItem添加到其他位置,因为当前collectionView尚未初始化。

一个想法的地方与willDisplay中的原始帖子一样,这很容易实现。唯一的问题是willDisplay的时间跨度很短,因此无法在那里重新加载整个collectionData。

但是,在此处滚动到一个indexPath很好。就像下面的代码一样。

 var onceOnly = false
 internal func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {

            if (!onceOnly){
            self.daysCollectionView.scrollToItem(at: self.selectedIndexPath!, at: .left, animated: false)
            onceOnly = true
            }
}

还有一个调用两个函数(selectIndex和滚动)的好地方,如下所示:

    override  func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    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 self.selectedIndexPath != nil {
      daysCollectionView.reloadItems(at: [self.selectedIndexPath!])
    }
    self.selectedIndexPath = indexPath;
     daysCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
     daysCollectionView.reloadItems(at: [indexPath])
}

现在我认为这是一个完整的答案。希望对您有所帮助。