Unable to simultaneously satisfy constraints when

2019-03-05 04:48发布

问题:

I have a stack view that look like the figure below:

So I change the height of the image programmatically to make it fit the image that download from my server,if dont have image,the height constraints of image will set to be zero.

Here is my code to doing so:

    let imageUrl = URL(string :imageString)

    if let data = try? Data(contentsOf: imageUrl!)
    {

       guard let actualImage: UIImage = UIImage(data: data) else{
        print("No image!")
        ImageView.image = nil
       defaultImageHeightContrainst = ImageHeightContrainst.constant
       ImageHeightContrainst.constant = 0

        layoutIfNeeded()
        return
      }

     let imageHeight = actualImage.size.height * actualImage.scale
     print("imageHeight = \(imageHeight)")
     defaultImageHeightContrainst = ImageHeightContrainst.constant
     ImageHeightContrainst.constant = imageHeight

     layoutIfNeeded()

     //here display the image to the imageview
      ImageView.kf.setImage(with: imageUrl)

   }

With the code above,the image height is scale according to actual image height which download from internet.If dont have image,the "image" part also set to 0 already.

This is what I expected,but now I face an error below whenever the "image" height become taller in order to fit the actual image height

 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:0x6080002868b0 UIImageView:0x7f9cbae59240.height == 50   (active)>",
    "<NSLayoutConstraint:0x608000286bd0 UIView:0x7f9cbae5ad80.height == 1   (active)>",
    "<NSLayoutConstraint:0x608000286d10 UIImageView:0x7f9cbae5b440.height == 458   (active)>",
    "<NSLayoutConstraint:0x608000286f90 UIView:0x7f9cbae5bb50.height == 1   (active)>",
    "<NSLayoutConstraint:0x6080002871c0 V:|-(0)-[UILabel:0x7f9cbae5b160]   (active, names: '|':UIStackView:0x7f9cbae5af60 )>",
    "<NSLayoutConstraint:0x608000287350 V:[UILabel:0x7f9cbae5b160]-(10)-[UIImageView:0x7f9cbae5b440]   (active)>",
    "<NSLayoutConstraint:0x6080002873a0 V:[UIImageView:0x7f9cbae5b440]-(10)-[UIStackView:0x7f9cbae5b670]   (active)>",
    "<NSLayoutConstraint:0x608000287580 V:[UIStackView:0x7f9cbae5b670]-(10)-[UIView:0x7f9cbae5bb50]   (active)>",
    "<NSLayoutConstraint:0x6080002875d0 V:[UIStackView:0x7f9cbae5c0f0]-(0)-|   (active, names: '|':UIStackView:0x7f9cbae5af60 )>",
    "<NSLayoutConstraint:0x608000287670 V:[UIView:0x7f9cbae5bb50]-(10)-[UIStackView:0x7f9cbae5c0f0]   (active)>",
    "<NSLayoutConstraint:0x6080002877b0 V:|-(10)-[UIImageView:0x7f9cbae59240]   (active, names: '|':UITableViewCellContentView:0x7f9cbae5a0c0 )>",
    "<NSLayoutConstraint:0x6080002878f0 V:[UIImageView:0x7f9cbae59240]-(8)-[UIView:0x7f9cbae5ad80]   (active)>",
    "<NSLayoutConstraint:0x6080002879e0 V:[UIStackView:0x7f9cbae5af60]-(10)-|   (active, names: '|':UITableViewCellContentView:0x7f9cbae5a0c0 )>",
    "<NSLayoutConstraint:0x608000287a80 V:[UIView:0x7f9cbae5ad80]-(8)-[UIStackView:0x7f9cbae5af60]   (active)>",
    "<NSLayoutConstraint:0x608000288c00 'UISV-canvas-connection' UIStackView:0x7f9cbae5c0f0.top == _UILayoutSpacer:0x6080001cd5c0'UISV-alignment-spanner'.top   (active)>",
    "<NSLayoutConstraint:0x608000288ca0 'UISV-canvas-connection' V:[_UILayoutSpacer:0x6080001cd5c0'UISV-alignment-spanner']-(0)-|   (active, names: '|':UIStackView:0x7f9cbae5c0f0 )>",
    "<NSLayoutConstraint:0x608000289970 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7f9cbae5a0c0.height == 476.5   (active)>"
)

Before asking,I already checked all my other constraints,which is all set correctly.I even disable all the code that can change the height of "image",once disable,there is no problem.The error only occurred when I intend to change the height.

I even tried to add ImageView.translatesAutoresizingMaskIntoConstraints = false before layoutIfNeeded(),but the error is still exist.

So what is the correct way to change the height of image in order to fit the actual image download from server?

回答1:

Considering that the last constraint says

"<NSLayoutConstraint:0x608000289970 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7f9cbae5a0c0.height == 476.5   (active)>"

I assume you are using stackView inside of a UITableViewCell to implement automatic height cells in a tableView. If my assumption is correct, then the problem is not with the stackView, nor with the imageView, but with the way UITableView works with UITableViewAutomaticDimension and Autolayout. If the layout works as you expect, and the warning is the only thing that bugs you, then read following.

Therefore it seems to me 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. The constraint mentioned above ('UIView-Encapsulated-Layout-Height') is the one applied by the UITableView that later goes away.

That means that the constraints you are using are probably OK. 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 layout trouble.

E.g., if you constrain the stackView to fit the cell's contentView, set the stackView.bottomAnchor to contentView.bottomAnchor just with the priority set to 999. If you did the layout programmatically, this might be your solution:

let bottomConstraint = tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
bottomConstraint.priority = UILayoutPriority(rawValue: 999)
NSLayoutConstraint.activate([
    // rest of the constraints
    stackView.topAnchor.constraint(equalTo: contentView.topAnchor),
    stackView.leftAnchor.constraint(equalTo: contentView.leftAnchor),
    stackView.rightAnchor.constraint(equalTo: contentView.rightAnchor),
    bottomConstraint,
])

If you do the layout in storyboards, just select appropriate constraint in the storyboards, and in the attributes inspector set its priority to 999 (for example):