1

I try to create a UITableViewCell and setup subviews as below:

class MyCellView: UITableViewCell {

    private let ImageView = UIImageView()
    private let titleView = UILabel()

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {

        super.init(style: style, reuseIdentifier: reuseIdentifier)

        contentView.addSubview(ImageView)
        contentView.addSubview(titleView)

        ImageView.translatesAutoresizingMaskIntoConstraints = false
        ImageView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
        ImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
        ImageView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
        ImageView.heightAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.6).isActive = true

        titleView.translatesAutoresizingMaskIntoConstraints = false
        titleView.topAnchor.constraint(equalTo: ImageView.bottomAnchor).isActive = true
        titleView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
        titleView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
        titleView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true

        titleView.numberOfLines = 0

        titleView.text = " "   // <-- *** please note this line

    }
}

And in the table view, I have set it to use dynamic height:

tableView.estimatedRowHeight = 80
tableView.rowHeight = UITableViewAutomaticDimension

My question is if I don't set the text for titleView in the cell, such as if I remark the last line in the cell view:

// titleView.text = " "

I will get constraint error from XCode. But if I set the text, everything works fine.

The text is dynamically downloaded from the Internet, so the text might be empty. Besides I can just set the text to " ", an empty space, I wonder if I misunderstand anything here? Can the text be empty ("" or nil) if I correct something?

Similarly, if I remove the code about titleView entirely, I just want to have an UIImageView in the cell, I found the cell's height is not expanded. How to set it up so that I can have an image of dynamic height in the cell?

EDIT: the constraint error:

2017-12-22 15:35:34.715865+0800 MyProject[15774:733515] [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:0x60c000095450 V:|-(0)-[UIImageView:0x7ff40df0de90]   (active, names: '|':UITableViewCellContentView:0x7ff40df0edc0 )>",
"<NSLayoutConstraint:0x60c0000955e0 UIImageView:0x7ff40df0de90.height == 0.6*UITableViewCellContentView:0x7ff40df0edc0.width   (active)>",
"<NSLayoutConstraint:0x60c000095630 V:[UIImageView:0x7ff40df0de90]-(0)-[UILabel:0x7ff40df0e0c0]   (active)>",
"<NSLayoutConstraint:0x60c0000957c0 UILabel:0x7ff40df0e0c0.bottom == UITableViewCellContentView:0x7ff40df0edc0.bottom   (active)>",
"<NSLayoutConstraint:0x60c000096210 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7ff40df0edc0.height == 248.333   (active)>",
"<NSLayoutConstraint:0x60c000095ea0 'UIView-Encapsulated-Layout-Width' UITableViewCellContentView:0x7ff40df0edc0.width == 414   (active)>"
)

Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x60c000095630 V:[UIImageView:0x7ff40df0de90]-(0)-[UILabel:0x7ff40df0e0c0]   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
Joe Huang
  • 6,296
  • 7
  • 48
  • 81

2 Answers2

2
  • If you want to avoid warning, try the code below

    class MyCellView: UITableViewCell {
    
      private let ImageView = UIImageView()
      private let titleView = UILabel()
    
      override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    
        contentView.addSubview(ImageView)
        contentView.addSubview(titleView)
    
        ImageView.translatesAutoresizingMaskIntoConstraints = false
        ImageView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
        ImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
        ImageView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
        let heightConstraint = ImageView.heightAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.6)
        heightConstraint.priority = UILayoutPriority.defaultLow
        heightConstraint.isActive = true
    
        titleView.translatesAutoresizingMaskIntoConstraints = false
        titleView.topAnchor.constraint(equalTo: ImageView.bottomAnchor).isActive = true
        titleView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
        titleView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
        titleView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
    
        titleView.numberOfLines = 0
    
    //    titleView.text = " "
      }
    }
    
  • If you remove the code about titleView entirely and want to have an UIImageView with dynamic height in the cell, adding bottomAnchor constraint for imageView

    class MyCellView: UITableViewCell {
    
      private let ImageView = UIImageView()
      private let titleView = UILabel()
    
      override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    
        contentView.addSubview(ImageView)
        contentView.addSubview(titleView)
    
        ImageView.translatesAutoresizingMaskIntoConstraints = false
        ImageView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
        ImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
        ImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
        ImageView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
        let heightConstraint = ImageView.heightAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.6)
        heightConstraint.priority = UILayoutPriority.defaultLow
        heightConstraint.isActive = true
      }
    }
    
trungduc
  • 11,926
  • 4
  • 31
  • 55
2

It seems that this is a result of an known "bug" - there is a collision of the height set by the tableView and the height calculated by the autolayout. When rendering the cell, the tableView first applies the default height, calculates the autolayout height, and then use the latter - at least it seems so. See my question.

That means that the constraints you are using are OK (at least they seem so to me). Just set one of the constraints defining height to priority = 999 (so that until it deactivates the default height constraint it won't cause any conflict). In the end, it will result in using your constraint anyway, so it will not cause any trouble.

P.S.: Watch out for that imageView height constraint:

ImageView.heightAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.6).isActive = true

If you'll want to omit the image (thus render it with height = 0), that will cause you troubles too (again, just lowering the priority might get you where you want). If the image is always there, then nevermind my P.S. :).

Milan Nosáľ
  • 19,169
  • 4
  • 55
  • 90