在 UITableViewCell 中添加 UICollectionView 后 UI 突然发生变化

Posted

技术标签:

【中文标题】在 UITableViewCell 中添加 UICollectionView 后 UI 突然发生变化【英文标题】:UI suddenly changes after adding UICollectionView inside UITableViewCell 【发布时间】:2020-03-03 06:57:26 【问题描述】:

当我尝试在 UITableViewCell 中添加 UICollectionView 时遇到一个奇怪的问题,UI 突然崩溃,我不知道为什么。

我尝试让它成为动态高度,但没有成功。

而且 CollectionView 也不滚动,我不知道为什么?当我隐藏 TableViewCell 时,CollectionView 的滚动效果很好。

滚动 TableView 之前:

滚动 TableView 后:

CollectionViewCell:

import UIKit

class CategoriesCollectionViewCell: UICollectionViewCell 
    override init(frame: CGRect) 
        super.init(frame: frame)
        backgroundColor = .white
        layoutUI()
    

    required init(coder adecoder: NSCoder) 
        fatalError("init(codeer:) has not been implemented")
    

    lazy var categoriesImage: UIImageView = 
        let categoriesImage = UIImageView()
        categoriesImage.contentMode = .scaleToFill
        categoriesImage.layer.cornerRadius = 8.0
        categoriesImage.image = UIImage(named: "pizza")
        categoriesImage.layer.cornerRadius = 8.0
        categoriesImage.layer.masksToBounds = true
        categoriesImage.translatesAutoresizingMaskIntoConstraints = false
        return categoriesImage
    ()

    lazy var containerView: UIView = 
        let containerView = UIView()
        containerView.backgroundColor = .black
        containerView.alpha = 0.7
        containerView.translatesAutoresizingMaskIntoConstraints = false
        return containerView
    ()

    lazy var categoryName: UILabel = 
        let categoryName = UILabel()
        categoryName.textColor = .white
        categoryName.font = UIFont(name: "AvenirNext-DemiBold", size: 16)
        categoryName.text = "Soup"
        categoryName.textAlignment = .left
        categoryName.translatesAutoresizingMaskIntoConstraints = false
        return categoryName
    ()

    lazy var recipesNumber: UILabel = 
        let recipesNumber = UILabel()
        recipesNumber.textColor = .white
        recipesNumber.font = UIFont(name: "AvenirNext-Regular", size: 16)
        recipesNumber.text = "33"
        recipesNumber.textAlignment = .left
        recipesNumber.translatesAutoresizingMaskIntoConstraints = false
        return recipesNumber
    ()

    func setupcategoriesImageConstraints() 
        NSLayoutConstraint.activate([
            categoriesImage.topAnchor.constraint(equalTo: topAnchor),
            categoriesImage.bottomAnchor.constraint(equalTo: bottomAnchor),
            categoriesImage.leadingAnchor.constraint(equalTo: leadingAnchor),
            categoriesImage.trailingAnchor.constraint(equalTo: trailingAnchor),
        ])
    

    func setupContainerViewConstraints() 
        NSLayoutConstraint.activate([
            containerView.topAnchor.constraint(equalTo: categoriesImage.topAnchor),
            containerView.bottomAnchor.constraint(equalTo: categoriesImage.bottomAnchor),
            containerView.leadingAnchor.constraint(equalTo: categoriesImage.leadingAnchor),
            containerView.trailingAnchor.constraint(equalTo: categoriesImage.trailingAnchor)
        ])
    

    func setupCategoryNameConstraints() 
        NSLayoutConstraint.activate([
            categoryName.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 16),
            categoryName.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16),
            categoryName.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: 16)
        ])
    

    func setuprecipesNumberConstraints() 
        NSLayoutConstraint.activate([
            recipesNumber.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -16),
            recipesNumber.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16),
            recipesNumber.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: 16)
        ])
    

    func addSubviews() 
        addSubview(categoriesImage)
        categoriesImage.addSubview(containerView)
        containerView.addSubview(categoryName)
        containerView.addSubview(recipesNumber)
    

    func layoutUI() 
        addSubviews()
        setupcategoriesImageConstraints()
        setupContainerViewConstraints()
        setupCategoryNameConstraints()
        setuprecipesNumberConstraints()
    

CollectionViewInTableViewCell:

import UIKit

class CategoriesTableViewCellCollectionViewCell: UITableViewCell, UICollectionViewDelegateFlowLayout 

    let categories = ["italian food", "chinese food", "korean food", "italian food", "chinese food", "korean food", "italian food", "chinese food", "korean food", "italian food", "chinese food", "korean food"]

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) 
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        layoutUI()
        selectionStyle = .none
    

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

    lazy var containerView: UIView = 
        let containerView = UIView()
        containerView.backgroundColor = .clear
        containerView.translatesAutoresizingMaskIntoConstraints = false
        return containerView
    ()

    lazy var categoriesNameLabel: UILabel = 
        let categoriesNameLabel = UILabel()
        categoriesNameLabel.text = "Categories"
        categoriesNameLabel.textColor = .customDarkGray()
        categoriesNameLabel.textAlignment = .left
        categoriesNameLabel.font = UIFont(name: "AvenirNext-Regular", size: 14)
        categoriesNameLabel.translatesAutoresizingMaskIntoConstraints = false
        return categoriesNameLabel
    ()

    lazy var seeAllCategoriesButton: UIButton = 
        let seeAllCategoriesButton = UIButton()
        seeAllCategoriesButton.setTitle("See all", for: .normal)
        seeAllCategoriesButton.setTitleColor(.CustomGreen(), for: .normal)
        seeAllCategoriesButton.titleLabel?.font = UIFont(name: "AvenirNext-Regular", size: 14)
        seeAllCategoriesButton.translatesAutoresizingMaskIntoConstraints = false
        seeAllCategoriesButton.addTarget(self, action: #selector(test), for: .touchUpInside)
        return seeAllCategoriesButton
    ()

    @objc func test() 
        print("Test worked")
    

    lazy var collectionView: UICollectionView = 
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.backgroundColor = .clear
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.register(CategoriesCollectionViewCell.self, forCellWithReuseIdentifier: "CategoriesCollectionViewCell")
        return collectionView
    ()

    func setupContainerViewConstraints() 
        NSLayoutConstraint.activate([
            containerView.topAnchor.constraint(equalTo: topAnchor, constant: 16),
            containerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            containerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
            containerView.heightAnchor.constraint(equalTo: categoriesNameLabel.heightAnchor)
        ])
    

    func setupCategoriesNameLabelConstraints() 
        NSLayoutConstraint.activate([
            categoriesNameLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
            categoriesNameLabel.centerYAnchor.constraint(equalTo: containerView.centerYAnchor)
        ])
    

    func setupSeeAllCategoriesButtonConstraints() 
        NSLayoutConstraint.activate([
            seeAllCategoriesButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
            seeAllCategoriesButton.centerYAnchor.constraint(equalTo: containerView.centerYAnchor)
        ])
    

    func setupCollectionViewConstraints() 
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: categoriesNameLabel.topAnchor, constant: 16),
            collectionView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 16),
            collectionView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            collectionView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
        ])
    

    func addSubviews() 
        addSubview(categoriesNameLabel)
        addSubview(containerView)
        containerView.addSubview(seeAllCategoriesButton)
        containerView.addSubview(collectionView)
    

    func layoutUI() 
        addSubviews()
        setupCollectionViewConstraints()
        setupContainerViewConstraints()
        setupCategoriesNameLabelConstraints()
        setupSeeAllCategoriesButtonConstraints()
    



extension CategoriesTableViewCellCollectionViewCell: UICollectionViewDelegate, UICollectionViewDataSource 

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
        return categories.count
    

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CategoriesCollectionViewCell", for: indexPath) as! CategoriesCollectionViewCell

        return cell
    

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


TableViewCell:

import UIKit

class HomeTableViewCell: UITableViewCell 

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) 
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        layoutUI()
        selectionStyle = .none
    

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

    lazy var containerView: UIView = 
        let containerView = UIView()
        containerView.backgroundColor = .white
        containerView.translatesAutoresizingMaskIntoConstraints = false
        containerView.layer.shadowColor = UIColor.black.cgColor
        containerView.layer.shadowOpacity = 1
        containerView.layer.shadowOffset = .init(width: 2, height: 2)
        containerView.layer.shadowRadius = 7.0
        containerView.layer.cornerRadius = 8.0
//        containerView.clipsToBounds = true
        return containerView
    ()

    lazy var foodImage: UIImageView = 
        let foodImage = UIImageView()
        foodImage.translatesAutoresizingMaskIntoConstraints = false
        foodImage.contentMode = .scaleAspectFill
        foodImage.clipsToBounds = true
        foodImage.layer.cornerRadius = 8.0
        return foodImage
    ()

    lazy var foodTitle: UILabel = 
        let foodTitle = UILabel()
        foodTitle.textColor = .CustomGreen()
        foodTitle.numberOfLines = 0
        foodTitle.translatesAutoresizingMaskIntoConstraints = false
        return foodTitle
    ()

    func setupContainerView() 
        NSLayoutConstraint.activate([
            containerView.topAnchor.constraint(equalTo: topAnchor, constant: 16),
            containerView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -16),
            containerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            containerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
        ])
    

    func setupFoodImage() 
        NSLayoutConstraint.activate([
            foodImage.topAnchor.constraint(equalTo: containerView.topAnchor),
            foodImage.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
            foodImage.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
            foodImage.heightAnchor.constraint(equalToConstant: 250)
        ])
    

    func setupFoodTitle() 
        NSLayoutConstraint.activate([
            foodTitle.topAnchor.constraint(equalTo: foodImage.bottomAnchor, constant: 16),
            foodTitle.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -16),
            foodTitle.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16),
            foodTitle.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -16)
        ])
    

    func addSubview() 
        addSubview(containerView)
        containerView.addSubview(foodImage)
        containerView.addSubview(foodTitle)
    

    func layoutUI() 
        addSubview()
        setupContainerView()
        setupFoodImage()
        setupFoodTitle()
    


主视图:

class HomeView: UIView 

    var recipes: Recipes?
    var recipesDetails = [Recipe]()
    let indicator = ActivityIndicator()

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

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

    lazy var foodTableView: UITableView = 
        let foodTableView = UITableView()
        foodTableView.translatesAutoresizingMaskIntoConstraints = false
        foodTableView.backgroundColor = .white
        foodTableView.delegate = self
        foodTableView.dataSource = self
        foodTableView.register(CategoriesTableViewCellCollectionViewCell.self, forCellReuseIdentifier: "CategoriesTableViewCellCollectionViewCell")
        foodTableView.register(HomeTableViewCell.self, forCellReuseIdentifier: "HomeTableViewCell")
        foodTableView.rowHeight = UITableView.automaticDimension
//        foodTableView.estimatedRowHeight = 100
        foodTableView.showsVerticalScrollIndicator = false
        foodTableView.separatorStyle = .none
        return foodTableView
    ()

    func setupFoodTableView() 
        NSLayoutConstraint.activate([
            foodTableView.topAnchor.constraint(equalTo: topAnchor),
            foodTableView.bottomAnchor.constraint(equalTo: bottomAnchor),
            foodTableView.leadingAnchor.constraint(equalTo: leadingAnchor),
            foodTableView.trailingAnchor.constraint(equalTo: trailingAnchor)
        ])
    

    func addSubview() 
        addSubview(foodTableView)
    

    func layoutUI() 
        indicator.setupIndicatorView(self, containerColor: .customDarkGray(), indicatorColor: .white)
        addSubview()
        setupFoodTableView()
        fetchData()

    

    func fetchData() 
        AF.request("https://api.url").responseJSON  (response) in
            if let error = response.error 
                print(error)
            
            do 
                self.recipes = try JSONDecoder().decode(Recipes.self, from: response.data!)
                self.recipesDetails = self.recipes?.recipes ?? []
                DispatchQueue.main.async 
                    self.foodTableView.reloadData()
                
             catch 
                print(error)
            
            self.indicator.hideIndicatorView()
        
    



extension HomeView: UITableViewDelegate, UITableViewDataSource 

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 

        return recipesDetails.count

    

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 

        if indexPath.row == 0 
            let cell = tableView.dequeueReusableCell(withIdentifier: "CategoriesTableViewCellCollectionViewCell", for: indexPath) as! CategoriesTableViewCellCollectionViewCell
            cell.frame = CGRect.zero
            cell.layoutIfNeeded()
            cell.collectionView.reloadData()
            return cell
        

        let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
        let url = URL(string: recipesDetails[indexPath.row].image ?? "Error")
        cell.foodImage.kf.setImage(with: url)
        cell.foodTitle.text = recipesDetails[indexPath.row].title
        cell.frame = CGRect.zero
        cell.layoutIfNeeded()
        return cell

    

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
        if indexPath.row == 0 
            return 160

         else 
            return 250
        

    


【问题讨论】:

由于我的工作量,我无法阅读所有这些代码,但我认为您应该更改约束的拥抱和抵抗优先级。也许collectionView 的拥抱优先级高于tableView 的。 @Ahmed Abd Elaziz 似乎你在cellForRowAt 处为 UITableView 和 UICollectionView 单元格设置为 cell.frame = CGRect.zero。这就是您的单元格框架在滚动期间发生变化的原因。 @emrcftci 我想是这样,但我实际上不知道该怎么做,因为这是我第一次以编程方式制作 UI。 @Ram 我应该从中删除或编辑什么? @AhmedAbdElaziz 对此发表评论并运行cell.frame = CGRect.zero 【参考方案1】:

通过删除cell.frame = CGRect.zero更新您的

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 

    if indexPath.row == 0 
        let cell = tableView.dequeueReusableCell(withIdentifier: "CategoriesTableViewCellCollectionViewCell", for: indexPath) as! CategoriesTableViewCellCollectionViewCell
        cell.layoutIfNeeded()
        cell.collectionView.reloadData()
        return cell
    

    let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
    let url = URL(string: recipesDetails[indexPath.row].image ?? "Error")
    cell.foodImage.kf.setImage(with: url)
    cell.foodTitle.text = recipesDetails[indexPath.row].title
    cell.layoutIfNeeded()
    return cell


【讨论】:

谢谢,布局是固定的,但 CollectionView 不滚动它固定不动。 @AhmedAbdElaziz 在numberOfItemsInSection 中有多少可用计数为categories 类别数组中有12个项目 categoriesNameLabel.heightAnchor 的值是多少?你能让它保持不变吗? 好的,你能看到集合视图的层次结构吗?检查所有关于滚动的属性,如滚动,启用分页,检查集合视图的框架,单元格内容视图,单元格 ui 元素并添加此代码collectionView.isScrollEnabled = true 并尝试

以上是关于在 UITableViewCell 中添加 UICollectionView 后 UI 突然发生变化的主要内容,如果未能解决你的问题,请参考以下文章

如何使用cmake强制UIC在源目录中生成ui_mainwindow.h

如何使用 setupUi 或 uic 输出来创建没有父小部件的独立布局?

UICollectionView 作为子视图不会更新约束

使用 PySide2 开发 Maya 插件系列二:继承 uic 转换出来的 py 文件中的类 Ui_Form

在 UITableViewCell 中添加 UICollectionView 时出错

在 UITableViewCell 子类中添加 CAGradientLayer 需要帮助