UITableViewCell 中的 UICollectionView 重用

Posted

技术标签:

【中文标题】UITableViewCell 中的 UICollectionView 重用【英文标题】:UICollectionView reuse in UITableViewCell 【发布时间】:2016-09-25 18:22:11 【问题描述】:

我知道这个问题已经被问过很多次了,但没有一个解决方案会起作用。

我在 uitableview 单元格中有集合视图,我在故事板中准备了一个表格视图和集合视图单元格,我的问题是假设我滚动单元格 1 集合视图,然后单元格 4 集合也自动滚动到该位置。我该如何处理这种情况..

请注意,我现在对屏幕进行静态设计

我的代码

// MARK:
    // MARK: table view delegate method

    func numberOfSectionsInTableView(tableView: UITableView) -> Int 
        return 1
    
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat 

        if Appconstant.isIpad()
        
            return 215.0
        
        return 185.0

    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return 6
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
        let strCell = "WorkoutCell" //+ "\(indexPath.row)"
        let cell = tableView.dequeueReusableCellWithIdentifier(strCell) as? WorkoutCell
//        if cell == nil
//        
//            cell = work 
//        
        return cell!
    
    func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) 
        let cell2 = cell as! WorkoutCell
        cell2.collecionWorkout.reloadData()
    
    // MARK:
    // MARK: collectionview delegate and data source
    //1
    func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool 
        return true
    
     func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int 
        return 1
    

    //2
     func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
        return 10
    
    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets 
        return UIEdgeInsetsMake(0.0, 10.0, 0.0, 10.0)
    

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: NSIndexPath) -> CGSize 
        // Adjust cell size for orientation
        if Appconstant.isIpad() 
            return CGSize(width: 160, height: 150)
        
        return CGSize(width: 140.0, height: 130.0)
    
    //3
    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell 
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("WorkoutCollectionCell", forIndexPath: indexPath)
        // Configure the cell
        return cell
    

uitableview 单元格代码

class WorkoutCell: UITableViewCell 

    @IBOutlet weak var collecionWorkout: UICollectionView!
    var flowLayout : UICollectionViewFlowLayout!
    override func awakeFromNib() 
        super.awakeFromNib()
        self.flowLayout = UICollectionViewFlowLayout()
        if Appconstant.isIpad()
        
            self.flowLayout.itemSize = CGSize(width: 160, height: 150)
        
        else
        
            self.flowLayout.itemSize = CGSize(width: 140, height: 130)
        
        self.flowLayout.scrollDirection = .Horizontal
        self.flowLayout.minimumInteritemSpacing = 10.0
        flowLayout.sectionInset = UIEdgeInsetsMake(0.0, 10.0, 0.0, 10.0);
        collecionWorkout.collectionViewLayout = flowLayout

        // Initialization code
    

    override func setSelected(selected: Bool, animated: Bool) 
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    


我滚动的第一个单元格

自动滚动的第 4 个单元格

【问题讨论】:

看起来像 thie 4。单元格被重用并从单元格 1 中获取所有内容。这是正确的,您必须对重用做出反应。在您的单元格类中,您需要覆盖 func,“prepareForReuse”,您应该重置内容、滚动位置或其他任何内容 @BjörnRo 但它也会影响实际的滚动单元格。我不认为这是解决方案 是的,没错,但这就是它的工作原理。如果您的单元格超出屏幕,那么它将被重新用于新单元格。所以它将使用之前单元格中的所有内容 这样做的方法只有你必须存储每个集合视图的偏移位置,并在每次重新加载表视图时设置。 在prepareforreuse中重置collection view的偏移量 【参考方案1】:

有两种情况。

1 - 重置 UICollectionView 偏移位置:

要重置 collectionview 的位置,只需在 cellForRowAtIndexPath 中调用 cell.collectionView.contentOffset = .zero

2 - 保持之前的滚动位置:

要保持之前的滚动位置,您需要在包含视图控制器的数据模型旁边存储每个单元格的偏移量列表。然后,您可以将给定单元格的偏移量保存在 didEndDisplayingCell 中,并将其设置为 cellForRowAtIndexPath 而不是 .zero

【讨论】:

对于第一个场景,我使用的是 prepareForReuse 函数。感谢您的建议。【参考方案2】:

UITableView 基于reusability 的概念。每当滚动tableView 时,tableView 中的cells 肯定会被重用。

现在出现了两种情况,

    获取新单元格 - 当tableViewCell 被重用时,其中的collectionView 也将被重用,所以这就是前一个单元格的contentOffset 的原因被保留。

    Scroll to previous cell - 如果我们手动滚动到 collectionView 中的特定 cell,我们可能希望在滚动回它时保留它的位置。

要在tableView 中获得这种功能,我们需要 - 手动将contentOffset 重置为1st case 并需要在第二个中保留contentOffset

您可以采用的另一种方法是使用 combination of UIScrollview and UIStackView

事情就是这样。

    storyboard 中,创建一个 UIScrollView。给它添加一个垂直的 UIStackView。

    stackView 中添加 4 个UICollectionViews。此数字可根据您的要求进行配置。

    将您的controller 添加为所有collectionViews 中的dataSourcedelegate

class ViewController: UIViewController, UICollectionViewDataSource 
    override func viewDidLoad() 
        super.viewDidLoad()
    

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

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CustomCell
        cell.label.text = "\(indexPath.row)"
        return cell
    


class CustomCell: UICollectionViewCell 
    @IBOutlet weak var label: UILabel!

现在,由于我们使用的是stackView,因此collectionViews 中的任何一个都不会被重用。因此,所有collectionViews 中的contentOffsets 将保持不变。

【讨论】:

如果集合视图数量永远不会相同,则可能难以管理此解决方案。在这种情况下,您需要创建一个集合视图数组,然后遍历它以填充堆栈视图。这似乎完全超越了表格视图的目的,不是吗? 当然这是唯一的缺点。在这种方法中,一切都必须手动处理。

以上是关于UITableViewCell 中的 UICollectionView 重用的主要内容,如果未能解决你的问题,请参考以下文章

collectionView:didSelectItemAtIndexPath:没有被调用

UITableViewCell 中的 UIDatePicker 更新 UITableViewCell 文本标签

更改 UITableViewCell 中的对象也更改了重用 UITableViewCell 的对象

确定 UITableViewCell 中的 UIButton

UITableViewCell 事件中的 UIButton 不起作用

UITableViewCell 中的 UICollectionView