在 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 输出来创建没有父小部件的独立布局?
使用 PySide2 开发 Maya 插件系列二:继承 uic 转换出来的 py 文件中的类 Ui_Form