Constraints on a UITableViewHeaderFooterView result in UIViewAlertForUnsatisfiableConstraints - ios

Been starring at this blankly for a bit now, probably too long, so I am trying a post here.
Here's the error I am seeing:
2017-11-02 22:43:23.972361-0700 TableViewCellHeaderViewLayoutTest[88247:17250641] [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:0x60c000092930 V:|-(12)-[UILabel:0x7fd450c0eb80'Header 1'] (active, names: '|':_UITableViewHeaderFooterContentView:0x7fd450c0f290 )>",
"<NSLayoutConstraint:0x60c0000927f0 UILabel:0x7fd450c0eb80'Header 1'.bottom == _UITableViewHeaderFooterContentView:0x7fd450c0f290.bottom - 6 (active)>",
"<NSLayoutConstraint:0x60c000092ac0 'UIView-Encapsulated-Layout-Height' _UITableViewHeaderFooterContentView:0x7fd450c0f290.height == 17.5 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x60c0000927f0 UILabel:0x7fd450c0eb80'Header 1'.bottom == _UITableViewHeaderFooterContentView:0x7fd450c0f290.bottom - 6 (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.
It's a super simple implementation of my UITableViewHeaderFooterView subclass:
class TableViewSectionHeaderView: UITableViewHeaderFooterView {
let titleLabel = UILabel()
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
contentView.backgroundColor = .gray
titleLabel.backgroundColor = contentView.backgroundColor
titleLabel.font = UIFont.preferredFont(forTextStyle: .footnote)
contentView.addSubview(titleLabel)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12).isActive = true
titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 6).isActive = true
titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -6).isActive = true
titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -6).isActive = true
}
}
The thing that stands out to me is this constraint:
"<NSLayoutConstraint:0x60c000092ac0 'UIView-Encapsulated-Layout-Height' _UITableViewHeaderFooterContentView:0x7fd450c0f290.height == 17.5 (active)>"
If I inspect it it also has a priority of 1000, which explains why my own constraints seems to fail.
Everything lays out fine visually, but I am concerned about that warning. What am I doing wrong?

It looks like the section header height is competing with the top and bottom constraints of the label, along with the intrinsic height of the actual label. The section header may need to grow or shrink depending on the height of the label, but the strict header height constraint is causing a conflict.
Set the sectionHeaderHeight and the estimatedSectionHeaderHeight to UITableViewAutomaticDimension to allow the height to adjust to the section's contents (the label in this case).

Related

Xcode UIStackView Weird LayoutConstraints Issue

I am adding a MapView (bascially a UIView) inside an empty UIStackView inside an empty UIScrollView. I implemented it the following way:
private func setupScrollView() {
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
let frameLayoutGuide = scrollView.frameLayoutGuide
NSLayoutConstraint.activate([
frameLayoutGuide.leadingAnchor.constraint(equalTo: view.leadingAnchor),
frameLayoutGuide.trailingAnchor.constraint(equalTo: view.trailingAnchor),
frameLayoutGuide.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
frameLayoutGuide.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
}
private func setupMainStackView() {
mainStackView.axis = .vertical
mainStackView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(mainStackView)
let contentLayoutGuide = scrollView.contentLayoutGuide
NSLayoutConstraint.activate([
mainStackView.widthAnchor.constraint(equalTo: view.widthAnchor),
mainStackView.leadingAnchor.constraint(equalTo: contentLayoutGuide.leadingAnchor),
mainStackView.trailingAnchor.constraint(equalTo: contentLayoutGuide.trailingAnchor),
mainStackView.topAnchor.constraint(equalTo: contentLayoutGuide.topAnchor),
mainStackView.bottomAnchor.constraint(equalTo: contentLayoutGuide.bottomAnchor)
])
setupMapView()
}
private func setupMapView() {
mapView.translatesAutoresizingMaskIntoConstraints = false
mapView.isUserInteractionEnabled = false
mainStackView.addArrangedSubview(mapView)
NSLayoutConstraint.activate([
// whenever the constant is set to a value other then zero, constraint issues pop up in the console
mapView.widthAnchor.constraint(equalTo: mainStackView.widthAnchor, constant: -10),
mapView.centerXAnchor.constraint(equalTo: mainStackView.centerXAnchor),
mapView.heightAnchor.constraint(equalTo: mapView.widthAnchor, multiplier: 0.618)
])
}
However, whenever the constant of the widthAnchor constraint of the mapView is set to any value other than zero, constraint issues pop up in the console. I am doing this because I want paddings on both the left and right edges of the mapView.
[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:0x282b37070 MKMapView:0x139835800.width == UIStackView:0x138a13480.width - 42.8 (active)>",
"<NSLayoutConstraint:0x282b372a0 MKMapView:0x139835800.centerX == UIStackView:0x138a13480.centerX (active)>",
"<NSLayoutConstraint:0x282b1c190 'UISV-canvas-connection' H:[MKMapView:0x139835800]-(0)-| (active, names: '|':UIStackView:0x138a13480 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x282b372a0 MKMapView:0x139835800.centerX == UIStackView:0x138a13480.centerX (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.
2020-12-26 19:01:44.048851-0800 LiQing Watercolor[16247:4990949] [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:0x282b37070 MKMapView:0x139835800.width == UIStackView:0x138a13480.width - 42.8 (active)>",
"<NSLayoutConstraint:0x282b1c140 'UISV-canvas-connection' UIStackView:0x138a13480.leading == MKMapView:0x139835800.leading (active)>",
"<NSLayoutConstraint:0x282b1c190 'UISV-canvas-connection' H:[MKMapView:0x139835800]-(0)-| (active, names: '|':UIStackView:0x138a13480 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x282b1c140 'UISV-canvas-connection' UIStackView:0x138a13480.leading == MKMapView:0x139835800.leading (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.
I also found that my code works perfectly when adding the mapView using mainStackView.addSubview(mapView) instead of mainStackView.addArrangedSubview(mapView).
If anyone knows how to resolve this issue that would be great! Thanks.

UITableView cell constraints problem - Swift

I started to get this constraints error:
"<NSLayoutConstraint:0x6000014419a0 V:|-(20)-[UIImageView:0x7f809779fae0] (active, names: '|':mChat.ChatCell:0x7f8097534600'ChatCell' )>",
"<NSLayoutConstraint:0x600001441b30 UIImageView:0x7f809779fae0.bottom == mChat.ChatCell:0x7f8097534600'ChatCell'.bottom - 20 (active)>",
"<NSLayoutConstraint:0x600001440eb0 UIImageView:0x7f809779fae0.height == 150 (active)>",
"<NSLayoutConstraint:0x600001440550 'UIView-Encapsulated-Layout-Height' mChat.ChatCell:0x7f8097534600'ChatCell'.height == 60.5 (active)>"
The problem appears when the UITableView has at least two images in it, then I get these constraints errors. Or when I add another image to the UITableView.
Here are my constraints:
func setupMediaMessage(){
mediaMessage.translatesAutoresizingMaskIntoConstraints = false
mediaMessage.contentMode = .scaleAspectFill
mediaMessage.backgroundColor = .lightGray
mediaMessage.layer.cornerRadius = 16
mediaMessage.layer.masksToBounds = true
let constraints = [
mediaMessage.topAnchor.constraint(equalTo: topAnchor, constant: 20),
mediaMessage.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20),
mediaMessage.widthAnchor.constraint(equalToConstant: 150),
mediaMessage.heightAnchor.constraint(equalToConstant: 150)
]
outcomingConstraint = mediaMessage.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 24)
incomingConstraint = mediaMessage.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -24)
NSLayoutConstraint.activate(constraints)
}
Don't worry about the trailing and leading constraints, I'm just checking if those images are incoming or outcoming.
My Github repo: https://github.com/realpaliy/mChat/blob/master/mChat/Controllers/Chats/ChatCell.swift. So, the UITableView looks good, but why do I get those errors?
If the layout looks correct, the conflicts are likely due to the internal auto-layout engine, and the order in which the constraints are evaluated.
Try this -- it should get rid of the warnings, without changing the end result:
let bAnchor = mediaMessage.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20)
bAnchor.priority = UILayoutPriority(rawValue: 999)
let constraints = [
mediaMessage.topAnchor.constraint(equalTo: topAnchor, constant: 20),
//mediaMessage.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20),
bAnchor,
mediaMessage.widthAnchor.constraint(equalToConstant: 150),
mediaMessage.heightAnchor.constraint(equalToConstant: 150)
]
EDIT
Note: I do not work for Apple - these are simply my observations:
When a cell is instantiated and / or dequeued, auto-layout uses the current row height to lay out the cell contents. It then uses the constraints on the cell contents to determine the actual row height. This can cause conflict warnings if the row is not tall enough for the generated height. By changing the Priority on the bottom-most constraint - or, I believe, on explicit element height constraints - to 999, that allows auto-layout to first break the constraint and then re-enforce it... without generating debug warnings.
You should (may?) also be able to eliminate the warnings by setting a big enough value for tableView.estimatedRowHeight.

Constraints break when programmatically customized UITableViewCell is removed from UITableView

I am using UITableViewController and I am loading customized UITableViewCell. All elements in the cell and the constraints are configured programmatically. The TableView looks like this
All works perfectly except when deleting a cell. I configured the tableView to fade cells when deleted. However, the cell gives a little jerk movement then it goes to the left instead of fading, and I get auto layout error messages in the Console: "Unable to simultaneously satisfy constraints" for below
"<NSLayoutConstraint:0x600002551a40 UIView:0x7fccbd5286f0.height == 100 (active)>",
"<NSLayoutConstraint:0x6000025505a0 V:|-(16)-[UIView:0x7fccbd5286f0] (active, names: '|':UITableViewCellContentView:0x7fccbd541b10 )>",
"<NSLayoutConstraint:0x600002550730 UIView:0x7fccbd5286f0.bottom == UITableViewCellContentView:0x7fccbd541b10.bottom (active)>",
"<NSLayoutConstraint:0x6000025605a0 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fccbd541b10.height == 1.19209e-07 (active)>"
and below
"<NSLayoutConstraint:0x6000025505a0 V:|-(16)-[UIView:0x7fccbd5286f0] (active, names: '|':UITableViewCellContentView:0x7fccbd541b10 )>",
"<NSLayoutConstraint:0x600002550730 UIView:0x7fccbd5286f0.bottom == UITableViewCellContentView:0x7fccbd541b10.bottom (active)>",
"<NSLayoutConstraint:0x6000025605a0 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fccbd541b10.height == 1.19209e-07 (active)>"
and that "Will attempt to recover by breaking constraint" for below
<NSLayoutConstraint:0x600002551a40 UIView:0x7fccbd5286f0.height == 100 (active)>
<NSLayoutConstraint:0x600002550730 UIView:0x7fccbd5286f0.bottom == UITableViewCellContentView:0x7fccbd541b10.bottom (active)>
I broke the console output into parts because I couldn't paste it all together for some reason.
Basically, I have a UIView called cellView (the white corner rounded area) inside the contentView of the cell. And all other elements you see are inside the cellView. I applied below constraints between the cellView and contentView
cellView.translatesAutoresizingMaskIntoConstraints = false
cellView.heightAnchor.constraint(equalToConstant: 100).isActive = true
cellView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16.0).isActive = true
cellView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
cellView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16.0).isActive = true
cellView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16.0).isActive = true
It worked when I tried deactivating all constraints in the cell and its subviews just before deleting the cell. But, when new cells are reused, they didn't have constraints to activate.
How should I handle this case?
You need to lower priority of bottom constraint
let con = cellView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
con.priority = UILayoutPriority(999)
con.isActive = true

iOS Facebook LoginButton constraints conflict

I want to resize facebook's LoginButton, but I am getting this error
[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.
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"NSAutoresizingMaskLayoutConstraint:0x1c4283ed0 h=--& v=--& FBSDKLoginButton: 0x104432fd0'Log in'.height == 40 (active)",
"NSLayoutConstraint:0x1c42828f0 FBSDKLoginButton:0x104432fd0'Log in'.height == 28 (active)>"
)
Will attempt to recover by breaking constraint
NSLayoutConstraint:0x1c42828f0 FBSDKLoginButton:0x104432fd0'Log in'.height == 28 (active)>
I've searched a lot, and have no idea how height == 28 constraint exists.
Creating LoginButton:
private var didLayoutSubviews = false {
didSet {
//I've use storyboard first, but couldn't make it work with resizing
self.facebookSignUpButton.removeFromSuperview()
self.facebookSignUpButton = LoginButton(readPermissions: [.email, .publicProfile])
//I've tried to set `isActive = false`
self.facebookSignUpButton.constraints.forEach({self.facebookSignUpButton.removeConstraint($0)})
self.facebookSignUpButton.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(self.facebookSignUpButton)
self.facebookSignUpButton.heightAnchor.constraint(equalToConstant: self.googleSignUpButton.frame.height - 8).isActive = true
self.facebookSignUpButton.widthAnchor.constraint(equalToConstant: self.googleSignUpButton.frame.width - 8).isActive = true
self.facebookSignUpButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
self.facebookSignUpButton.topAnchor.constraint(equalTo: self.separatorLabel.bottomAnchor, constant: 20).isActive = true
self.facebookSignUpButton.bottomAnchor.constraint(equalTo: self.googleSignUpButton.topAnchor, constant: -10).isActive = true
self.facebookSignUpButton.delegate = self.presenter
self.view.layoutIfNeeded()
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if !self.didLayoutSubviews {
self.didLayoutSubviews = true
}
}
The notification to developers is in the Facebook Guidelines for 4.19 . This has already been discussed on SO here.
Personally, I was only able to make this work with a custom login button in Swift 4.

Swift - UIView with constraint of zero height - error

I have UIView with height constraint. I want to hide this view, so I set height constraint constant to 0.
I am setting constraint in code inside extended UIView:
_height = self.heightAnchor.constraint(equalToConstant: 50)
NSLayoutConstraint.activate([
_closeBtn.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 5),
_closeBtn.topAnchor.constraint(equalTo: self.topAnchor, constant: 10),
_closeBtn.heightAnchor.constraint(equalTo: _centerLabel.heightAnchor),
_closeBtn.widthAnchor.constraint(equalTo: _closeBtn.heightAnchor),
_centerLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 10),
_centerLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -5),
_centerLabel.leadingAnchor.constraint(equalTo: _closeBtn.trailingAnchor),
_centerLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16),
_height
])
Now, If I change _height.constant = 0, I have got this error:
[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:0x146a374c0 UIButton:0x147615b00'X'.width == UIButton:0x147615b00'X'.height (active)>",
"<NSLayoutConstraint:0x146a37530 UIButton:0x147615b00'X'.height == UILabel:0x147615e80.height (active)>",
"<NSLayoutConstraint:0x146a37450 V:|-(20)-[UILabel:0x147615e80] (active, names: '|':InternetConnectionInfoView:0x14742f600 )>",
"<NSLayoutConstraint:0x146a373e0 UILabel:0x147615e80.bottom == InternetConnectionInfoView:0x14742f600.bottom - 5 (active)>",
"<NSLayoutConstraint:0x146a37680 InternetConnectionInfoView:0x14742f600.height == 0 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x146a374c0 UIButton:0x147615b00'X'.width == UIButton:0x147615b00'X'.height (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.
[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:0x146a37530 UIButton:0x147615b00'X'.height == UILabel:0x147615e80.height (active)>",
"<NSLayoutConstraint:0x146a37450 V:|-(20)-[UILabel:0x147615e80] (active, names: '|':InternetConnectionInfoView:0x14742f600 )>",
"<NSLayoutConstraint:0x146a373e0 UILabel:0x147615e80.bottom == InternetConnectionInfoView:0x14742f600.bottom - 5 (active)>",
"<NSLayoutConstraint:0x146a37680 InternetConnectionInfoView:0x14742f600.height == 0 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x146a373e0 UILabel:0x147615e80.bottom == InternetConnectionInfoView:0x14742f600.bottom - 5 (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.
I suspect, that this comes from top and bottom anchor being set to constant values. I have tried to use greaterThanOrEual / lessThanOrEqual, but it destroys the layout.
How to solve this (eg. hiding UIView)? I cannot set isHidden, because other UIViews are anchored to the bottom of this view and they need to move upon hiding.
This is a constraint conflict, consider reducing the constraint priority can help you fix this issue.
let closeBtnTopAnchor = _closeBtn.topAnchor.constraint(greaterThanOrEqualTo: self.topAnchor)
let closeBtnTopAnchorWithLowPriority = _closeBtn.topAnchor.constraint(equalTo: self.topAnchor, constant: 10)
closeBtnTopAnchorWithLowPriority.priority = UILayoutPriorityDefaultHigh
let centerLabelTopAnchor = _centerLabel.topAnchor.constraint(greaterThanOrEqualTo: self.topAnchor)
let centerLabelTopAnchorWithLowPriority = _centerLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 10)
centerLabelTopAnchorWithLowPriority.priority = UILayoutPriorityDefaultHigh
let centerLabelBottomAnchor = _centerLabel.bottomAnchor.constraint(lessThanOrEqualTo: self.bottomAnchor)
let centerLabelBottomAnchorWithLowPriority = _centerLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -5)
centerLabelBottomAnchorWithLowPriority.priority = UILayoutPriorityDefaultHigh
NSLayoutConstraint.activate([
_closeBtn.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 5),
closeBtnTopAnchor,
closeBtnTopAnchorWithLowPriority,
_closeBtn.heightAnchor.constraint(equalTo: _centerLabel.heightAnchor),
_closeBtn.widthAnchor.constraint(equalTo: _closeBtn.heightAnchor),
centerLabelTopAnchor,
centerLabelTopAnchorWithLowPriority,
centerLabelBottomAnchor,
centerLabelBottomAnchorWithLowPriority,
_centerLabel.leadingAnchor.constraint(equalTo: _closeBtn.trailingAnchor),
_centerLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16),
_height
])
If hiding the UIView is enough for you, how about setting its alpha to 0 ?

Resources