嵌入在 UITableView 中的 UICollectionView 中的 UICollectionViewCells 未按预期显示

Posted

技术标签:

【中文标题】嵌入在 UITableView 中的 UICollectionView 中的 UICollectionViewCells 未按预期显示【英文标题】:UICollectionViewCells in a UICollectionView embedded in UITableView do not appear as desired 【发布时间】:2018-05-14 00:22:32 【问题描述】:

我在 UICollectionViewController 的集合视图中显示自定义 UICollectionViewCells 时遇到问题。

潜在的重要因素:

我正在以编程方式创建布局,而不是使用情节提要 集合视图作为 UITableViewCell 的子视图嵌入到我的主 UIViewController 的 UITableView 中。 我正在使用水平方向的 UICollectionViewFlowLayout 单元格填满其父单元的整个宽度和高度。

我在测试中注意到的事情:

在应用程序的当前状态下,cellForItemAt indexPath 仅对应显示的第一个单元格调用一次。 点击第一个单元格时,它会消失。我还没有找到让它重新出现的方法。 我可以滑动到其他单元格应该在的位置,并且那里有正确数量的“空槽”,但单元格本身没有出现。

我已尝试仅包含我认为必要的代码以减小这篇文章的大小,但如果有任何我没有考虑到的地方,请告诉我。感谢您提供任何帮助或建议。此外,我希望在将来在这种情况下使用 xcode 进行调试时得到一些指导。有谁知道任何指南或书籍可以提供寻找此类问题的提示?我对 gdb/visual studio 的调试工具有一些经验,但是在调试方面,Xcode 和框架对我来说有点困难。再次感谢。

表格视图的定义和约束:

...

let trainingTableViewController = TrainingTableViewController()

lazy var trainingTableView: UITableView = 
    let tableView = UITableView()
    tableView.dataSource = trainingTableViewController
    tableView.delegate = trainingTableViewController
    tableView.register(TrainingLogoCell.self, forCellReuseIdentifier: "trainingLogoID")
    tableView.register(TrainingProgressCell.self, forCellReuseIdentifier: "trainingProgressID")
    tableView.register(TrainingProgressPageControlCell.self, forCellReuseIdentifier: "trainingProgressPageControlID")
    tableView.register(TrainingWeekCell.self, forCellReuseIdentifier: "trainingWeekID")
    tableView.separatorStyle = .none
    tableView.backgroundColor = .clear
    tableView.translatesAutoresizingMaskIntoConstraints = false
    return tableView
()

override func viewDidLoad() 
    super.viewDidLoad()

    ...

    view.addSubview(trainingTableView)
    trainingTableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true // TODO: see about anchoring to bottom of navigation
    trainingTableView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 25).isActive = true
    trainingTableView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -25).isActive = true
    trainingTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true

带有 CollectionView 单元格的 TableView 类

class TrainingTableViewController: NSObject, UITableViewDelegate, UITableViewDataSource 

    ...

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        if indexPath.section == 0 
            let cell = tableView.dequeueReusableCell(withIdentifier: "trainingLogoID", for: indexPath)
            return cell
         else if indexPath.section == 1 
            let cell = tableView.dequeueReusableCell(withIdentifier: "trainingProgressID", for: indexPath)
            return cell
        
        else if indexPath.section == 2 
            let cell = tableView.dequeueReusableCell(withIdentifier: "trainingProgressPageControlID", for: indexPath)
            return cell
         else 
            let cell = tableView.dequeueReusableCell(withIdentifier: "trainingWeekID", for: indexPath)
            return cell
        
    

    ...        

class TrainingProgressCell: UITableViewCell 
    let trainingProgressCollectionView: UICollectionView = 
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        layout.minimumLineSpacing = 0
        let collectionViewController = TrainingProgressCollectionViewController(collectionViewLayout: layout)
        collectionViewController.collectionView!.isPagingEnabled = true
        collectionViewController.collectionView!.backgroundColor = .clear
        collectionViewController.collectionView!.translatesAutoresizingMaskIntoConstraints = false
        return collectionViewController.collectionView!
    ()

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) 
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        contentView.heightAnchor.constraint(equalToConstant: 250).isActive = true
        backgroundColor = UIColor.init(rgb: 0x333333)
        layer.cornerRadius = 10
        selectionStyle = UITableViewCellSelectionStyle.none

        contentView.addSubview(trainingProgressCollectionView)
        trainingProgressCollectionView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
        trainingProgressCollectionView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
        trainingProgressCollectionView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
        trainingProgressCollectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
    

    required init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    


...

我的 UICollectionView 类

import UIKit

private let reuseIdentifier = "trainingProgressCollectionViewID"

class TrainingProgressCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout 
    override func viewDidLoad() 
        super.viewDidLoad()

        self.collectionView!.register(TrainingProgressCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
    

    override func numberOfSections(in collectionView: UICollectionView) -> Int 
        return 1
    

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

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        print(String(indexPath.row))
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! TrainingProgressCollectionViewCell
        cell.backgroundColor = .red
        cell.dayLabel.text = "Day " + String(indexPath.row)
        return cell
    

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
        return CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
    


class TrainingProgressCollectionViewCell: UICollectionViewCell 
    var constraintsSetupDone = false

    let dayLabel: UILabel = 
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textColor = .white
        label.font = UIFont(name: "Montserrat-ExtraBold", size: 18)
        return label
    ()

    override init(frame: CGRect) 
        super.init(frame: frame)

        contentView.addSubview(dayLabel)
        dayLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
        dayLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
    

    required init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

【问题讨论】:

当 collectionView 返回时,你的 collectionView 控制器会死掉。因此,委托和数据源将变为 nil,因为它很弱并且不存在对控制器的引用。这不是好的代码或好的编码习惯:( 【参考方案1】:

需要确保集合视图控制器在范围内。它的范围最初仅限于定义集合视图的代码块,因此,集合视图不再具有数据源或委托。

【讨论】:

以上是关于嵌入在 UITableView 中的 UICollectionView 中的 UICollectionViewCells 未按预期显示的主要内容,如果未能解决你的问题,请参考以下文章

嵌入在 UITableView 中的 UICollectionView 中的 UICollectionViewCells 未按预期显示

嵌入在 UICollectionViewCell 中的自定尺寸 UITableView

UITableView 在嵌入在容器中的控制器之间传递数据

使用 swrevealviewcontroller 在 ViewController 中嵌入 uitableView 中的 Segues

嵌入在 UIStackView 中的 UITableView 不会重新加载数据

自动调整嵌入在 UITableView 中的 UICollectionView 单元格不起作用