I’ve coded simple project to show my problem. I created a UITableView, whose top, bottom, left, right pinned to view of ViewController. There is only one cell in UITableView. Inside the cell, there is a UIImageView, whose top, bottom, left and right pinned to those of cell. The cell’s height is AutoDimension. Here my code below:
class ProductSelectionVC: UIViewController {
weak var tableView: UITableView!
weak var test: UITableViewCell!
override func viewDidLoad() {
super.viewDidLoad()
//Initialize Table View
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
let tableViewLayouts = [
tableView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
tableView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 0),
tableView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: 0)
]
self.view.addSubview(tableView)
NSLayoutConstraint.activate(tableViewLayouts)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
tableView.estimatedRowHeight = 44
tableView.allowsSelection = false
tableView.dataSource = self
tableView.delegate = self
self.tableView = tableView
}
}
extension ProductSelectionVC : UITableViewDelegate , UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = QuantityAddCell()
cell.product = chanelCoCo
cell.contentView.layer.borderWidth = 4
cell.contentView.layer.borderColor = UIColor.red.cgColor
cell.loadSubViews()
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
class QuantityAddCell: UITableViewCell{
var product: Product!
weak var testImageView: UIImageView!
func loadSubViews() {
let productPicsView = UIImageView()
productPicsView.translatesAutoresizingMaskIntoConstraints = false
let productPicsViewLayouts = [
productPicsView.topAnchor.constraint(equalTo: self.contentView.topAnchor),
productPicsView.heightAnchor.constraint(equalTo: productPicsView.widthAnchor, multiplier: 3/8, constant: 0),
productPicsView.leftAnchor.constraint(equalTo: self.contentView.leftAnchor),
productPicsView.rightAnchor.constraint(equalTo: self.contentView.rightAnchor),
productPicsView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor)
]
self.contentView.addSubview(productPicsView)
self.testImageView = productPicsView
NSLayoutConstraint.activate(productPicsViewLayouts)
}
}
If you look at my code, I have set the ratio of UIImageView is 8:3 by set multiplier of 3/8 for the heightAnchor constraint with widthAnchor. Now if I run stimulator, it run completely fine but the debug console inform the me the notices:
2020-11-23 08:13:55.359711+0700 ECommercialTest[1208:24062] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x6000031a5bd0 V:|-(0)-[UIImageView:0x7fdef6a1c3b0] (active, names: '|':UITableViewCellContentView:0x7fdef6a1be50 )>",
"<NSLayoutConstraint:0x6000031a5c20 UIImageView:0x7fdef6a1c3b0.height == 0.375*UIImageView:0x7fdef6a1c3b0.width (active)>",
"<NSLayoutConstraint:0x6000031a5c70 H:|-(0)-[UIImageView:0x7fdef6a1c3b0](LTR) (active, names: '|':UITableViewCellContentView:0x7fdef6a1be50 )>",
"<NSLayoutConstraint:0x6000031a5cc0 UIImageView:0x7fdef6a1c3b0.right == UITableViewCellContentView:0x7fdef6a1be50.right (active)>",
"<NSLayoutConstraint:0x6000031a5d10 UIImageView:0x7fdef6a1c3b0.bottom == UITableViewCellContentView:0x7fdef6a1be50.bottom (active)>",
"<NSLayoutConstraint:0x6000031a0960 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fdef6a1be50.height == 141 (active)>",
"<NSLayoutConstraint:0x6000031a13b0 'UIView-Encapsulated-Layout-Width' UITableViewCellContentView:0x7fdef6a1be50.width == 375 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x6000031a5c20 UIImageView:0x7fdef6a1c3b0.height == 0.375*UIImageView:0x7fdef6a1c3b0.width (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
If you look at the console error. You could see that the width is 375 (the stimulator is iPhone 11 Pro Max) and the height is 140.666667. Meanwhile, with my desired ratio of 8:3, the height should be 140.625 (375 * 3 / 8). I believe it is the reason why the constraints conflict.
But the funny thing is if I replace 3/8 with 2, 3, 5 or any not-decimal numbers; or any fraction that 375 could divide and produce no remainder, for example 5, the height would be 75. The console are happy with no comments, no error. So constraints only struggle with multiplier produce remainder. Again, the stimulator is running fine.
My Xcode is 12
Would anyone helping with this problem please?
3
Answers
Firstly, you use xib for
UITableViewCell
, then you can use auto layout in xib.Secondly, in your cell:
This make conflict because
UIImageView
had constraint height, width equalcontentView
of cell. So,productPicsView.heightAnchor.constraint(equalTo: productPicsView.widthAnchor, multiplier: 3/8, constant: 0)
not need. Or if you want keep multiplier ofUIImageView
you need removeproductPicsView.leftAnchor.constraint(equalTo: self.contentView.leftAnchor)
I think you should change the priority of Image’s height constraint
so try to set it 750 or 250 that might resolve the issue
UIKit will not try to draw on "partial pixels"
As you noted, your specified ratio of
8:3
on a device with375
point width results in a height of 140.625 (375 * 3 / 8).So, you need to tell auto-layout which constraint you want to allow it to modify. In most cases such as yours, that would be the image view’s height.
This will get rid of the auto-layout complaints.
As a side note, you’re doing a number of things wrong.
1 – You want to register your custom cell class for reuse. In your code, you’re registering a default
UITableViewCell
— but then never actually using it.2 – Following that comment, your
cellForRowAt
is not dequeuing a cell for reuse.3 – You
cellForRowAt
is callingcell.loadSubViews()
… but all of that code should be in your cell’s initialization.Here’s an updated version of your code:
Edit
A little clarification…
When using Automatic Dimension for table view row heights (the default), auto-layout doesn’t like partial pixels.
You certainly can constrain a
UIImageView
(by itself) to a height of375 * 3 / 8
(140.625) and you won’t get auto-layout complaints.Also, if you explicitly set the row height:
You won’t get auto-layout complaints.
However, we very often want the row height to be set by the cell’s content, which is the whole point.