Ios 以编程方式添加NSLayout约束的问题
我在当前项目中使用greaterThanOrEqualTo约束时遇到问题 我需要的是使单元格的高度动态,因此我需要根据API返回的内容将配方的标题设置为多行,并使收藏夹按钮接受trailingAnchor的常量约束。但我看到的是截图 如果我使用XIB,我会很容易做到,但这是我第一次以编程方式制作UI HomeTableViewCell:Ios 以编程方式添加NSLayout约束的问题,ios,swift,uitableview,uitableviewautomaticdimension,Ios,Swift,Uitableview,Uitableviewautomaticdimension,我在当前项目中使用greaterThanOrEqualTo约束时遇到问题 我需要的是使单元格的高度动态,因此我需要根据API返回的内容将配方的标题设置为多行,并使收藏夹按钮接受trailingAnchor的常量约束。但我看到的是截图 如果我使用XIB,我会很容易做到,但这是我第一次以编程方式制作UI HomeTableViewCell: class HomeTableViewCell: UITableViewCell { override init(style: UITableVie
class HomeTableViewCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
layoutUI()
selectionStyle = .none
self.backgroundColor = .clear
}
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.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 favouriteButton: UIButton = {
var favouriteButton = UIButton()
favouriteButton.setImage(UIImage(systemName: "heart"), for: .normal)
favouriteButton.tintColor = .red
favouriteButton.translatesAutoresizingMaskIntoConstraints = false
return favouriteButton
}()
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: self.bounds.width / 1.8)
])
}
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(greaterThanOrEqualTo: favouriteButton.leadingAnchor, constant: -16)
])
}
func setupFavouriteButtonConstraints() {
NSLayoutConstraint.activate([
favouriteButton.centerYAnchor.constraint(equalTo: foodTitle.centerYAnchor),
favouriteButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -16)
])
}
func addSubview() {
addSubview(containerView)
containerView.addSubview(foodImage)
containerView.addSubview(foodTitle)
containerView.addSubview(favouriteButton)
}
func layoutUI() {
addSubview()
setupContainerView()
setupFoodImage()
setupFoodTitle()
setupFavouriteButtonConstraints()
}
}
class HomeView: UIView {
var recipes: Recipes?
var recipesDetails = [Recipe]()
let indicator = ActivityIndicator()
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( 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 = #colorLiteral(red: 0.9568627451, green: 0.9568627451, blue: 0.9568627451, alpha: 1)
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("apilink.com").responseJSON { (response) in
if let error = response.error {
print(error)
}
do {
if let data = response.data {
self.recipes = try JSONDecoder().decode(Recipes.self, from: 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.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
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0 {
return 160
} else {
return UITableView.automaticDimension
}
}
}
家庭视图:
class HomeTableViewCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
layoutUI()
selectionStyle = .none
self.backgroundColor = .clear
}
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.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 favouriteButton: UIButton = {
var favouriteButton = UIButton()
favouriteButton.setImage(UIImage(systemName: "heart"), for: .normal)
favouriteButton.tintColor = .red
favouriteButton.translatesAutoresizingMaskIntoConstraints = false
return favouriteButton
}()
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: self.bounds.width / 1.8)
])
}
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(greaterThanOrEqualTo: favouriteButton.leadingAnchor, constant: -16)
])
}
func setupFavouriteButtonConstraints() {
NSLayoutConstraint.activate([
favouriteButton.centerYAnchor.constraint(equalTo: foodTitle.centerYAnchor),
favouriteButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -16)
])
}
func addSubview() {
addSubview(containerView)
containerView.addSubview(foodImage)
containerView.addSubview(foodTitle)
containerView.addSubview(favouriteButton)
}
func layoutUI() {
addSubview()
setupContainerView()
setupFoodImage()
setupFoodTitle()
setupFavouriteButtonConstraints()
}
}
class HomeView: UIView {
var recipes: Recipes?
var recipesDetails = [Recipe]()
let indicator = ActivityIndicator()
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( 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 = #colorLiteral(red: 0.9568627451, green: 0.9568627451, blue: 0.9568627451, alpha: 1)
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("apilink.com").responseJSON { (response) in
if let error = response.error {
print(error)
}
do {
if let data = response.data {
self.recipes = try JSONDecoder().decode(Recipes.self, from: 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.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
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0 {
return 160
} else {
return UITableView.automaticDimension
}
}
}
您可以将foodTitleLabel尾部更改为equalTo将解决此问题。使用标签和收藏夹按钮。但是,如果您仍然不希望它是相等的,那么在这种情况下,由于您已经使用了负值,您知道您没有反转您的约束,您必须使用lessThanOrEqualTo来实现您想要做的事情。这与你在故事板中可能做的完全相反
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(lessThanOrEqualTo: favouriteButton.leadingAnchor, constant: -16)
])
}
同样,在主视图中,您正在设置tablview使用这些,您已经有了处理自动动态的代码,但是您已经注释掉了估计的高度,也没有注释
foodTableView.estimatedRowHeight = 100
foodTableView.rowHeight = UITableView.automaticDimension
更新:
您必须更改foodTitleLabel上的内容拥抱和内容压缩优先级
foodTitle.setContentHuggingPriority(.init(240.0), for: .horizontal)
foodTitle.setContentCompressionResistancePriority(.init(740.0), for: .horizontal)
内容拥抱优先级:
设置视图拒绝被设置为大于其固有大小的优先级
内容压缩优先级:
设置视图拒绝小于其固有大小的优先级
这就是为什么现在的布局试图使你最喜欢的按钮小于标题标签的原因为什么要使用
大于或等于作为标签的尾部约束?简单地将其更改为equalTo
可能会解决您的问题。另外,对约束中的常量使用负值可能会导致混淆,如果需要,请始终尝试反转约束,以使常量为正值。@pckill我使用它根据配方名称创建containerView动态是的,但这是一个尾部约束,可以修复。标签的高度应该定义动态单元格高度,而不是其宽度。@pckill以及如何使标签的高度动态?我尝试基于大于或等于实现动态高度,但仍然存在相同的问题您是否尝试使用UIStackView?仍然存在相同的问题,我在iPhone XR和iPhone SE上进行了测试。iPhone SE:iPhone XR:@AhmedAbdElaziz更改内容优先级它将开始工作,正如我在您现在上传的新屏幕截图中看到的,它存在内容优先级问题。非常感谢您的帮助。它工作得很好。