Ios 如何通过indexPath在水平collectionView中动态添加视图/层

Ios 如何通过indexPath在水平collectionView中动态添加视图/层,ios,swift,uicollectionview,uicollectionviewcell,Ios,Swift,Uicollectionview,Uicollectionviewcell,我制作了一个带有水平集合视图的时间线,每个时间段(一个月)我都有一个单元格,每个单元格都有不同颜色的UIView。有些月份增加了30个视图,有些是31个,有些是28个。我在动态地将视图添加到每个单元格时遇到问题,这样它们就不会被复制或添加到错误的单元格中 为此,我创建了这个时间线的简化版本,每个月只动态添加1个层/视图-然后我将尝试添加可变数量的视图/层 我把这个项目作为我所说的最简单的例子: 以下是ViewController.swift中的代码: import UIKit class V

我制作了一个带有水平集合视图的时间线,每个时间段(一个月)我都有一个单元格,每个单元格都有不同颜色的UIView。有些月份增加了30个视图,有些是31个,有些是28个。我在动态地将视图添加到每个单元格时遇到问题,这样它们就不会被复制或添加到错误的单元格中

为此,我创建了这个时间线的简化版本,每个月只动态添加1个层/视图-然后我将尝试添加可变数量的视图/层

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

以下是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,它们恰好是外部边缘,因为NumberOfItemsSection和1 section返回了28个单元格。所以我希望每个细胞都是浅蓝色的,除了第一个和第28个浅红色

然而,正如你在这里看到的。当我来回滚动时,单元格被填充了多次,有蓝色、红色和紫色的多种色调,当然不是1和28

我认为有两个问题

  • 不知何故,indexath.item返回1或28,即使我没有滚动到边缘单元格。为什么会这样
  • 当我重新访问已渲染的单元时,它会重新渲染它们。我不知道这是为什么。我想知道重写prepareForReuse()是否有帮助,但我听说这通常是个坏主意,所以我不确定这是否是我想要的
    对实现这一目标有什么建议吗

    你忽略了细胞被重用的事实。当你改变一个单元格的填充颜色时,它被滚动掉了,被滚动掉的单元格会重新使用这个单元格,你只是把它的填充颜色设置为红色,而你没有关闭它。为每个单元格明确设置fillColor,无论是红色、白色还是透明色,不要只为选定的单元格设置它。

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

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

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

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

    其他几点注意事项:

    • 最好总是将UI添加到
      单元格.contentView
      ,而不是
      单元格本身
    • 一旦你运行它,考虑把这个代码移到你的代码> uICLoopDeVIEWCEDCELL 子类。如果没有子类,可能需要一个单独的函数,这样就不会用讨厌的If-else逻辑混乱
      cellForItemAt

    这确实有帮助,但每次我滚动回红细胞时,红细胞似乎仍被覆盖——基本上是添加了另一层。此外,如果您添加两个不同的层,这取决于它在[1,28]中是否像我在更新代码中所做的那样,它通常会同时绘制两个层-紫色。我该怎么做才能防止这种情况发生?每次你在cellForItemAt中被呼叫时,你都必须从头开始设置你的手机,并移除手机中可能上次放入的任何东西。因此,如果单元格应该着色,请删除旧图层颜色并添加新图层。如果单元格不应着色,请删除旧的图层颜色并将单元格恢复到其原始状态。是否为单元格本身设置awakeFromNib?是cell类的变量部分吗?我需要视图是动态的-为了能够为时间线创建数量可变的视图,对不起,我将在问题中澄清。awakeFromNib是UICollectionViewCell中的一个方法。您需要在自定义单元格中覆盖它。初始化tickLayer并在cellForItemAtIndexPath函数中更改其颜色。全局变量不是处理层的正确方法。请注意,层是视图(单元)的一部分-每个单元都应该有自己的层副本,它将有自己的子层,同时还要记住单元是重用的。拥有一个层的全局变量只需从先前处理的单元格中删除tickLayer,并将其添加到当前单元格中。这可能导致未定义的行为。能否更新GitHub项目?里面什么也没有
    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)     
    }