如何通过indexPath

时间:2018-06-06 16:54:58

标签: ios swift uicollectionview uicollectionviewcell

我使用水平集合制作各种时间轴。对于每个时间块(一个月),我有一个带有UIViews的单元格,每天都有不同的颜色。有些月份增加了30个视图,大约31个视图,大约28个。我无法动态地向每个单元格添加视图,因此它们不会重复或添加到错误的单元格中。

为此,我创建了这个时间轴的简化版本,其中每个月只有1层/视图动态添加 - 我将尝试解决添加可变数量的视图/图层的问题。

我把这个项目作为我所说的最简单的例子:

https://github.com/AlexMarshall12/testTimeline-iOS

以下是ViewController.swift中的代码:

import UIKit

class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {

    var filledCells: [Int] = []

    @IBOutlet weak var collectionView: UICollectionView!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.filledCells = [1,28]
        collectionView.delegate = self
        collectionView.dataSource = self
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 28
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myCell", for: indexPath) as! MyCollectionViewCell
        print(indexPath.item)
        cell.myLabel.text = String(indexPath.item)
        if filledCells.contains(indexPath.item) {
            let tickLayer = CAShapeLayer()
            tickLayer.path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: cell.layer.bounds.width, height: cell.layer.bounds.height), cornerRadius: 5).cgPath
            tickLayer.fillColor = UIColor(red:0.99, green:0.13, blue:0.25, alpha:0.5).cgColor
            cell.layer.addSublayer(tickLayer)
        } else {

        //updated 

            let tickLayer = CAShapeLayer()
            tickLayer.path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: cell.layer.bounds.width, height: cell.layer.bounds.height), cornerRadius: 5).cgPath
            tickLayer.fillColor = UIColor(red:0.1, green:0.13, blue:0.98, alpha:0.5).cgColor
            cell.layer.addSublayer(tickLayer)
        }
        return cell
    }

}

这个想法是,对于每个indexPath项(每个单元?),它会查看是否包含在self.filledCells数组中:1或28恰好是外边缘,因为为numberOfItemsInSection返回了28个单元格, 1节。所以我想要发生的是每个细胞都是淡蓝色,除了第一和第二 - 浅红色。

但是,您可以在此处看到https://imgur.com/a/KTLn7Cb。当我来回滚动时,细胞被多次填充,有多种颜色的蓝色,红色和紫色,当然还有1和28以外的颜色。

我认为有两个问题。

  1. 即使我没有滚动到最边缘的单元格,indexPath.item也会返回1或28。为什么是这样?
  2. 当我重新访问已经渲染的单元格时,它会重新渲染它们。我不确定为什么会这样。我想知道覆盖prepareForReuse()是否有帮助,但我听说这通常是一个坏主意,所以我不确定它是否是我正在寻找的。
  3. 有关实现这一目标的任何建议吗?

4 个答案:

答案 0 :(得分:3)

您忽略了细胞被重复使用的事实。当你更改一个单元格fillColor,并且它被滚动时,滚动的单元格会重新使用该单元格,而你只需将其填充颜色设置为红色,而你没有将其关闭。为每个单元格明确设置fillColor,无论是红色,白色还是清晰的颜色,都不要为所选单元格设置它。

答案 1 :(得分:2)

每次在表格视图单元格或集合视图单元格中添加视图或图层总是一个坏主意。当您重复使用单元格时,无需一次又一次地创建 tickLayer 。将tickLayer作为全局变量,在MyCollectionViewCell中的awakeFromNib函数中初始化它,然后在cellForRow函数中更改其填充颜色。

答案 2 :(得分:0)

向单元格添加/删除视图不是一个好主意。 有两种方法可以完成此任务: ①通过子视图创建不同的单元格; ②创建一个通用单元格并通过隐藏和布局控制子视图的显示,并在获得具有新数据的单元格时进行更新。

答案 3 :(得分:0)

再加上@Owen Hartnett关于单元格可重用性的说法,我想指出你每次都在重新创建图层!这相当于创建子视图。

每次遇到cellForItemAt函数内的单元格时,您还应检查是否存在预先添加的图层。像(在语法上不完美的Swift,但伪代码):

let bLayerFound = false;
for (layer: CALayer in cell.layer.sublayers)
{
   if (layer.name.equals("mylayer"))
   {
     let shapeLayer = layer as! CAShapeLayer
     bLayerFound = true;
     //set layer properties based on your business logic, as you don't know what this cell is holding due to reuse
     shapeLayer.path = //path
     shapeLayer.fillColor = //color
   }
}

if (bLayerFound == false)
{
   //create layer here, again
   let tickLayer = CAShapeLayer()

   //set layer properties based on your business logic, as you don't know what this cell is holding due to reuse
   tickLayer.path = //path
   tickLayer.fillColor = //color
   tickLayer.name = "mylayer"
   cell.layer.addSublayer(tickLayer)     
}

其他一些警告:

  • 最好将UI添加到cell.contentView,而不是cell本身。
  • 一旦运行,请考虑将此代码移至UICollectionViewCell子类。如果没有子类,可能是一个单独的函数,以免混淆cellForItemAt带有令人讨厌的if-else逻辑。