【问题标题】:Nest UICollectionView into UITableViewCell将 UICollectionView 嵌套到 UITableViewCell 中
【发布时间】:2021-02-10 23:47:56
【问题描述】:

我正在尝试构建一些东西

我正在尝试使用 UICollectionView 构建标签列表视图并将其嵌套到我的自定义 UITableViewCell 中。

我现在有什么

上网一搜,找到了问题的关键:

  • 子类 UICollectionView 并实现它的内在内容大小属性。

但是,当我将自定义 UICollectionView 嵌套到自调整大小的 UITableViewCell 中时,整个事情就无法正常工作。布局已损坏。

无论我如何更改代码,我都会得到以下 3 个有问题的 UI 之一。

collection view 的高度总是不对的,要么太小,要么太大,不能恰到好处地拥抱它的内容。

当我使用Debug View Hierarchy查看视图时,我发现虽然UI坏了,但集合视图的contentSize属性值是正确的。好像内容大小属性不能及时反映到UI上。

class IntrinsicCollectionView: UICollectionView {
    
    override var contentSize: CGSize {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }
    
    override var intrinsicContentSize: CGSize {
        layoutIfNeeded()
        return CGSize(width: UIView.noIntrinsicMetric, height: collectionViewLayout.collectionViewContentSize.height)
    }
    
    override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
        super.init(frame: frame, collectionViewLayout: layout)
        isScrollEnabled = false
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

关于如何创建具有内在内容大小的自定义 UICollectionView 有很多解决方案。其中一些可以正常工作。但是当将它们嵌套到 UITableViewCell 中时,它们都不能正常工作。

还有一些关于只将一个 UICollectionView 嵌套到 UITableViewCell 中而没有其他视图的答案。但是如果UITableViewCell中还有一些UILabel,那就不行了。

我将所有代码上传到 github。 https://github.com/yunhao/nest-collectionview-in-tableviewcell

谢谢!

【问题讨论】:

    标签: ios uicollectionview autolayout


    【解决方案1】:

    我会试着解释发生了什么......

    为了便于理解,在您的 ListViewController 中,让我们从一行开始:

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1 // items.count
    }
    

    在您的ListViewCell 类中,将这些行添加到prepareViews() 的末尾:

        // so we can see the element frames
        titleLabel.backgroundColor = .green
        subtitleLabel.backgroundColor = .cyan
        collectionView.backgroundColor = .yellow
    

    在您的IntrinsicCollectionView 类中,让我们添加一个print() 语句以提供一些信息:

    override var intrinsicContentSize: CGSize {
        layoutIfNeeded()
        
        // add this line
        print("collView Width:", frame.width, "intrinsic height:", collectionViewLayout.collectionViewContentSize.height)
        
        return CGSize(width: UIView.noIntrinsicMetric, height: collectionViewLayout.collectionViewContentSize.height)
    }
    

    当我在 iPhone 8 上运行该应用程序时,我得到了以下结果:

    我在调试控制台中看到了这个:

    collView Width: 66.0 intrinsic height: 350.0
    collView Width: 343.0 intrinsic height: 30.0
    

    这告诉我,集合视图被要求提供其intrinsicContentSize它有一个完整的框架。

    此时,它填充其单元格,其布局以350.collectionViewContentSize.height 结束(这一行有六个“标签”单元格)。

    自动布局然后执行另一遍...集合视图现在具有有效的框架宽度(基于单元格宽度)...并且单元格被重新布局。

    不幸的是,表格视图已经根据初始集合视图intrinsicContentSize.height 设置了行高。

    因此,可能(应该)解决此问题的两个步骤:

    ListViewCell中,获取标签时集合视图的内容大小无效:

    func setTags(_ tags: [String]) {
        self.tags = tags
        collectionView.reloadData()
        
        // add this line
        collectionView.invalidateIntrinsicContentSize()
    }
    

    然后,在ListViewController 中,我们需要重新加载表格它的框架已经改变:

    // add this var
    var currentWidth: CGFloat = 0
    
    // implement viewDidLayoutSubviews()
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        if view.frame.width != currentWidth {
            currentWidth = view.frame.width
            tableView.reloadData()
        }
    }
    

    这似乎(通过非常快速的测试)给了我可靠的结果:

    关于设备旋转:

    【讨论】:

      猜你喜欢
      • 2021-12-16
      • 2019-07-13
      • 1970-01-01
      • 1970-01-01
      • 2021-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多