I am building a chat layout and i have a table view and custom chat cell by adding constraint programmatically but i am getting the autolayout warning
In my ChatMessageCell i have a enum Style to determine the chat layout direction leading or trailing
I have messageLabel which has top, bottom, leading or trailing anchor to the UITableViewCelland width less than equal to 250
I have view which has leading, trailing, bottom and top anchor to the messageLabel view, setMessageLayout method active or deactive the leading or trailing anchor of the view
Autolayout Warning
(
"<NSLayoutConstraint:0x6000039a3c00 UILabel:0x7ff54cc34b20'Hello how are you'.width <= 250 (active)>",
"<NSLayoutConstraint:0x6000039a3e80 H:[UILabel:0x7ff54cc34b20'Hello how are you']-(16)-| (active, names: '|':iOSChatApp.ChatMessageCell:0x7ff54d087600'cell_leading' )>",
"<NSLayoutConstraint:0x6000039a3e30 iOSChatApp.ChatMessageCell:0x7ff54d087600'cell_leading'.leading == UILabel:0x7ff54cc34b20'Hello how are you'.leading - 16 (active)>",
"<NSLayoutConstraint:0x6000039afca0 'UIView-Encapsulated-Layout-Width' iOSChatApp.ChatMessageCell:0x7ff54d087600'cell_leading'.width == 375 (active)>"
)
ChatMessageCell
class ChatMessageCell: UITableViewCell {
enum Style {
case leading
case trailing
}
private var style: Style!
private let messageLbl = UILabel()
private let view = UIView()
private var leadingConstraint: NSLayoutConstraint!
private var trailingConstraint: NSLayoutConstraint!
var message: ChatMessage! {
didSet {
messageLbl.text = message.message
setMessageLayout(with: message.sender == "User1" ? .leading : .trailing)
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = #colorLiteral(red: 0.9490196078, green: 0.9450980392, blue: 0.9529411765, alpha: 1)
messageLbl.translatesAutoresizingMaskIntoConstraints = false
messageLbl.font = UIFont(name: "Avenir-Book", size: 17)
messageLbl.numberOfLines = 0
view.translatesAutoresizingMaskIntoConstraints = false
view.layer.cornerRadius = 5
addSubview(view)
addSubview(messageLbl)
NSLayoutConstraint.activate([
topAnchor.constraint(equalTo: messageLbl.topAnchor, constant: -16),
bottomAnchor.constraint(equalTo: messageLbl.bottomAnchor, constant: 16),
messageLbl.widthAnchor.constraint(lessThanOrEqualToConstant: 250)
])
NSLayoutConstraint.activate([
view.topAnchor.constraint(equalTo: messageLbl.topAnchor, constant: -8),
view.bottomAnchor.constraint(equalTo: messageLbl.bottomAnchor, constant: 8),
view.leadingAnchor.constraint(equalTo: messageLbl.leadingAnchor, constant: -8),
view.trailingAnchor.constraint(equalTo: messageLbl.trailingAnchor, constant: 8),
])
leadingConstraint = leadingAnchor.constraint(equalTo: messageLbl.leadingAnchor, constant: -16)
trailingConstraint = trailingAnchor.constraint(equalTo: messageLbl.trailingAnchor, constant: 16)
leadingConstraint.isActive = true
trailingConstraint.isActive = false
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setMessageLayout(with style: Style) {
messageLbl.textColor = style == .leading ? .black : .white
view.backgroundColor = style == .leading ? .white : #colorLiteral(red: 0.06274509804, green: 0.6039215686, blue: 0.3019607843, alpha: 1)
leadingConstraint.isActive = style == .leading ? true : false
trailingConstraint.isActive = style == .trailing ? true : false
}
}
My Layout
According to your Autolayout warning, messageLabel's width and leading are conflicting.
The warning is telling that both status are active
(
"<NSLayoutConstraint:0x6000039a3c00 UILabel:0x7ff54cc34b20'Hello how are you'.width <= 250 (active)>",
"<NSLayoutConstraint:0x6000039a3e80 H:[UILabel:0x7ff54cc34b20'Hello how are you']-(16)-| (active, names: '|':iOSChatApp.ChatMessageCell:0x7ff54d087600'cell_leading' )>",
"<NSLayoutConstraint:0x6000039a3e30 iOSChatApp.ChatMessageCell:0x7ff54d087600'cell_leading'.leading == UILabel:0x7ff54cc34b20'Hello how are you'.leading - 16 (active)>",
"<NSLayoutConstraint:0x6000039afca0 'UIView-Encapsulated-Layout-Width' iOSChatApp.ChatMessageCell:0x7ff54d087600'cell_leading'.width == 375 (active)>"
)
If you read warning carefully, the console may recommend you what to remove.
Related
I've made UI for cell in cell class below:
final class OptionTVCell: UITableViewCell {
fileprivate static let id = String(describing: OptionTVCell.self)
private var defaultTintColor: UIColor {
let color = AppConfiguration.sharedAppConfiguration.appTextColor
return Utility.hexStringToUIColor(hex: color ?? "ffffff")
}
// MARK: - Subviews
lazy private(set) var optionImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.heightAnchor.constraint(equalToConstant: 24).isActive = true
imageView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
imageView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
let widthConstraint = imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor)
widthConstraint.priority = UILayoutPriority(998)
widthConstraint.isActive = true
imageView.contentMode = .scaleAspectFit
imageView.tintColor = defaultTintColor
return imageView
}()
lazy private(set) var optionNameLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.setContentHuggingPriority(.defaultLow, for: .horizontal)
label.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
label.textColor = defaultTintColor
label.numberOfLines = 0
let fontSize: CGFloat = Constants.shared.IPHONE ? 14 : 20
label.font = UIFont(name: Utility.getFontName(), size: fontSize)
return label
}()
lazy private(set) var separatorLineView: UIView = {
let separator = UIView()
separator.translatesAutoresizingMaskIntoConstraints = false
let heightConstraint = separator.heightAnchor.constraint(equalToConstant: 1)
heightConstraint.priority = UILayoutPriority(999)
heightConstraint.isActive = true
separator.backgroundColor = defaultTintColor.withAlphaComponent(0.5)
return separator
}()
lazy private(set) var separatorContainerStackView: UIStackView = {
let verticalStackView = UIStackView()
verticalStackView.translatesAutoresizingMaskIntoConstraints = false
verticalStackView.axis = .vertical
verticalStackView.spacing = 10
verticalStackView.distribution = .fillProportionally
verticalStackView.alignment = .fill
return verticalStackView
}()
lazy private(set) var iconContainerStackView: UIStackView = {
let horizontalStackView = UIStackView()
horizontalStackView.translatesAutoresizingMaskIntoConstraints = false
horizontalStackView.axis = .horizontal
horizontalStackView.spacing = 16
horizontalStackView.distribution = .fill
horizontalStackView.alignment = .center
return horizontalStackView
}()
// MARK: - Init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Life Cycle
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
super.setHighlighted(highlighted, animated: animated)
let color = highlighted
? Utility.hexStringToUIColor(hex: AppConfiguration.sharedAppConfiguration.primaryHoverColor ?? "ffffff")
: defaultTintColor
optionImageView.tintColor = color
optionNameLabel.textColor = color
}
// MARK: - Setup
private func setupView() {
selectionStyle = .none
backgroundColor = .clear
contentView.backgroundColor = .clear
setupIconStackView()
setupSeparatorStackView()
}
private func setupSeparatorStackView() {
contentView.addSubview(separatorContainerStackView)
NSLayoutConstraint.activate([
separatorContainerStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
separatorContainerStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
separatorContainerStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8),
separatorContainerStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16)
])
separatorContainerStackView.addArrangedSubview(iconContainerStackView)
separatorContainerStackView.addArrangedSubview(separatorLineView)
}
private func setupIconStackView() {
iconContainerStackView.addArrangedSubview(optionImageView)
iconContainerStackView.addArrangedSubview(optionNameLabel)
let labelWidth = optionNameLabel.widthAnchor.constraint(greaterThanOrEqualTo: iconContainerStackView.widthAnchor, constant: -40)
labelWidth.isActive = true
}
// MARK: - Configure
}
Below is the tableView implementation for cell:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return CGFloat(cellHeight)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard
let cell = tableView.dequeueReusableCell(withIdentifier: OptionTVCell.id) as? OptionTVCell,
indexPath.row <= optionsArray.count-1,
let navItem = optionsArray[indexPath.row] as? OptionItem
else {
return UITableViewCell()
}
cell.separatorLineView.isHidden = navItem.hasSeparator == true ? false : true
if let imageName = navItem.pageIcon, !imageName.isEmpty {
if let optionImage = UIImage(named: imageName)?.withRenderingMode(.alwaysTemplate) {
cell.optionImageView.image = optionImage
cell.optionImageView.isHidden = false
} else {
let listImageStringURL = imageName.appending("?w=\(Utility.sharedUtility.getImageSizeAsPerScreenResolution(size: cell.optionImageView.frame.size.width)))&h=\(Utility.sharedUtility.getImageSizeAsPerScreenResolution(size: cell.optionImageView.frame.size.height))")
if let imageURL = URL(string: listImageStringURL) {
cell.optionImageView.af.setImage(
withURL: imageURL,
placeholderImage: nil,
filter: nil,
imageTransition: .crossDissolve(0.2)
)
}
}
} else {
cell.optionImageView.isHidden = true
}
cell.optionNameLabel.text = navItem.title?.uppercased()
return cell
}
As you can see the separator view and icon can be hidden or shown and modifies cell height accordingly. The issue is the self sizing does not work on initial dequeue but after scrolling it fixes itself.
Here is the runtime error thrown from autolayout:
1.
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:0x600002525810 'fittingSizeHTarget' UIStackView:0x7fafd1e8c330.width == 0 (active)>",
"<NSLayoutConstraint:0x600002524f50 'UISV-canvas-connection' UIStackView:0x7fafd1e8c330.leading == UIImageView:0x7fafd1e8c4c0.leading (active)>",
"<NSLayoutConstraint:0x600002524fa0 'UISV-canvas-connection' H:[UILabel:0x7fafd1e8aa30]-(0)-| (active, names: '|':UIStackView:0x7fafd1e8c330 )>",
"<NSLayoutConstraint:0x600002524ff0 'UISV-spacing' H:[UIImageView:0x7fafd1e8c4c0]-(16)-[UILabel:0x7fafd1e8aa30] (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600002524ff0 'UISV-spacing' H:[UIImageView:0x7fafd1e8c4c0]-(16)-[UILabel:0x7fafd1e8aa30] (active)>
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:0x600002500410 V:|-(8)-[UIStackView:0x7fafd1f47c90] (active, names: '|':UITableViewCellContentView:0x7fafd1f0cf90 )>",
"<NSLayoutConstraint:0x600002500820 UIStackView:0x7fafd1f47c90.bottom == UITableViewCellContentView:0x7fafd1f0cf90.bottom - 8 (active)>",
"<NSLayoutConstraint:0x600002500550 UIImageView:0x7fafd1f569f0.height == 24 (active)>",
"<NSLayoutConstraint:0x600002502120 'UISV-canvas-connection' V:[_UILayoutSpacer:0x60000399e2b0'UISV-alignment-spanner']-(0)-| (active, names: '|':UIStackView:0x7fafd1f5a1e0 )>",
"<NSLayoutConstraint:0x600002500370 'UISV-canvas-connection' UIStackView:0x7fafd1f5a1e0.centerY == UIImageView:0x7fafd1f569f0.centerY (active)>",
"<NSLayoutConstraint:0x600002500000 'UISV-canvas-connection' UIStackView:0x7fafd1f47c90.top == UIStackView:0x7fafd1f5a1e0.top (active)>",
"<NSLayoutConstraint:0x6000025d2260 'UISV-canvas-connection' V:[UIView:0x7fafd1f48db0]-(0)-| (active, names: '|':UIStackView:0x7fafd1f47c90 )>",
"<NSLayoutConstraint:0x6000025b2490 'UISV-spacing' V:[UIStackView:0x7fafd1f5a1e0]-(10)-[UIView:0x7fafd1f48db0] (active)>",
"<NSLayoutConstraint:0x600002502210 'UISV-spanning-boundary' _UILayoutSpacer:0x60000399e2b0'UISV-alignment-spanner'.bottom >= UIImageView:0x7fafd1f569f0.bottom (active)>",
"<NSLayoutConstraint:0x6000025f2cb0 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fafd1f0cf90.height == 43.5 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600002500550 UIImageView:0x7fafd1f569f0.height == 24 (active)>
What I did:
I have added a few constraints and reduced priority as they were giving some errors when made hidden but they had fixed height/width. But errors are still thrown and self sizing still not working properly.
You should be able to get rid of your layout errors / warnings with a single change:
lazy private(set) var separatorContainerStackView: UIStackView = {
let verticalStackView = UIStackView()
verticalStackView.translatesAutoresizingMaskIntoConstraints = false
verticalStackView.axis = .vertical
verticalStackView.spacing = 10
// use .fill NOT .fillProportionally
verticalStackView.distribution = .fill // .fillProportionally
verticalStackView.alignment = .fill
return verticalStackView
}()
A tip: when working with UIStackView, forget about the .fillProportionally distribution setting. There are very specific layouts where that is appropriate, but you are unlikely to encounter them. And, as you see, it can cause problems when used incorrectly.
As side notes:
lazy private(set) var optionImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.heightAnchor.constraint(equalToConstant: 24).isActive = true
// you can use this
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor).isActive = true
// none of this is needed
//imageView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
//imageView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
//let widthConstraint = imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor)
//widthConstraint.priority = UILayoutPriority(998)
//widthConstraint.isActive = true
imageView.contentMode = .scaleAspectFit
imageView.tintColor = defaultTintColor
return imageView
}()
and:
lazy private(set) var optionNameLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
// these two are not needed
//label.setContentHuggingPriority(.defaultLow, for: .horizontal)
//label.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
label.textColor = defaultTintColor
label.numberOfLines = 0
let fontSize: CGFloat = Constants.shared.IPHONE ? 14 : 20
label.font = UIFont(name: Utility.getFontName(), size: fontSize)
return label
}()
I'm trying to do UI programatically and I'm getting weird constraint errors, but when I run the app, it it looks as expected.
What I'm trying to do:
I have a ViewController TodayVC where I have a UIView and I'm trying to render content of MWStepsActivityVC in that view.
Here is my TodayVC:
class TodayVC: UIViewController {
let scrollView = UIScrollView()
let contentView = UIView()
let stepsActivityView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
configureViewController()
configureScrollView()
configureContainerViews()
layoutUI()
}
func configureViewController() {
view.backgroundColor = .systemBackground
}
func configureScrollView() {
view.addSubview(scrollView)
scrollView.addSubview(contentView)
scrollView.pinToEdges(of: view)
contentView.pinToEdges(of: scrollView)
NSLayoutConstraint.activate([
contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
contentView.heightAnchor.constraint(equalToConstant: 600)
])
}
func configureContainerViews() {
self.add(childVC: MWStepsActivityVC(activityType: .steps), to: self.stepsActivityView)
}
func layoutUI() {
contentView.addSubview(stepsActivityView)
stepsActivityView.translatesAutoresizingMaskIntoConstraints = false
let padding: CGFloat = 20
let itemHeight: CGFloat = 140
NSLayoutConstraint.activate([
stepsActivityView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: padding),
stepsActivityView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -padding),
stepsActivityView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding),
stepsActivityView.heightAnchor.constraint(equalToConstant: itemHeight),
])
print("TodayVC view width \(view.frame.size.width)")
}
func add(childVC: UIViewController, to containerView: UIView) {
addChild(childVC)
containerView.addSubview(childVC.view)
childVC.view.frame = containerView.bounds
childVC.didMove(toParent: self)
}
}
UIViewExtension:
extension UIView {
func addSubviews(_ views: UIView...) {
for view in views {
addSubview(view)
}
}
func pinToEdges(of superview: UIView) {
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
topAnchor.constraint(equalTo: superview.topAnchor),
leadingAnchor.constraint(equalTo: superview.leadingAnchor),
trailingAnchor.constraint(equalTo: superview.trailingAnchor),
bottomAnchor.constraint(equalTo: superview.bottomAnchor),
])
}
}
MWStepsActivityVC:
class MWActivityVC: UIViewController {
let iconImageView = UIImageView()
let titleLabel = UILabel()
let counterLabel = UILabel()
init(activityType: ActivityType) {
super.init(nibName: nil, bundle: nil)
self.set(activityType: activityType)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
configureBackgroundView()
layoutUI()
placeholderData()
}
fileprivate func placeholderData() {
iconImageView.image = SFSymbols.steps
titleLabel.text = "StepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsSteps"
counterLabel.text = "9000"
counterLabel.backgroundColor = .red
}
fileprivate func configureBackgroundView() {
view.layer.cornerRadius = 18
}
fileprivate func layoutUI() {
view.addSubviews(iconImageView, titleLabel, counterLabel)
iconImageView.translatesAutoresizingMaskIntoConstraints = false
titleLabel.translatesAutoresizingMaskIntoConstraints = false
counterLabel.translatesAutoresizingMaskIntoConstraints = false
let padding: CGFloat = 20
NSLayoutConstraint.activate([
iconImageView.topAnchor.constraint(equalTo: view.topAnchor, constant: padding),
iconImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: padding),
iconImageView.heightAnchor.constraint(equalToConstant: 20),
iconImageView.widthAnchor.constraint(equalToConstant: 20),
titleLabel.centerYAnchor.constraint(equalTo: iconImageView.centerYAnchor),
titleLabel.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor, constant: padding),
titleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -padding),
titleLabel.heightAnchor.constraint(equalToConstant: 20),
counterLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -padding),
counterLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: padding),
counterLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -padding),
counterLabel.heightAnchor.constraint(equalToConstant: 40)
])
print("MWStepsActivityVC view width \(view.frame.size.width)")
}
}
I'm getting these errors:
"<NSLayoutConstraint:0x600002228320 H:|-(20)-[UIImageView:0x7fc0abe077a0] (active, names: '|':UIView:0x7fc0abd10850 )>",
"<NSLayoutConstraint:0x6000022283c0 UIImageView:0x7fc0abe077a0.width == 20 (active)>",
"<NSLayoutConstraint:0x600002228460 H:[UIImageView:0x7fc0abe077a0]-(20)-[UILabel:0x7fc0abe08a70] (active)>",
"<NSLayoutConstraint:0x6000022284b0 UILabel:0x7fc0abe08a70.trailing == UIView:0x7fc0abd10850.trailing - 20 (active)>",
"<NSLayoutConstraint:0x600002230b90 'UIView-Encapsulated-Layout-Width' UIView:0x7fc0abd10850.width == 0 (active)>",
"<NSLayoutConstraint:0x600002228320 H:|-(20)-[UIImageView:0x7fc0abe077a0] (active, names: '|':UIView:0x7fc0abd10850 )>",
"<NSLayoutConstraint:0x600002228460 H:[UIImageView:0x7fc0abe077a0]-(20)-[UILabel:0x7fc0abe08a70] (active)>",
"<NSLayoutConstraint:0x6000022284b0 UILabel:0x7fc0abe08a70.trailing == UIView:0x7fc0abd10850.trailing - 20 (active)>",
"<NSLayoutConstraint:0x600002230b90 'UIView-Encapsulated-Layout-Width' UIView:0x7fc0abd10850.width == 0 (active)>",
"<NSLayoutConstraint:0x6000022285a0 H:|-(20)-[UILabel:0x7fc0abe08ce0] (active, names: '|':UIView:0x7fc0abd10850 )>",
"<NSLayoutConstraint:0x6000022285f0 UILabel:0x7fc0abe08ce0.trailing == UIView:0x7fc0abd10850.trailing - 20 (active)>",
"<NSLayoutConstraint:0x600002230b90 'UIView-Encapsulated-Layout-Width' UIView:0x7fc0abd10850.width == 0 (active)>"
I tried to use the wtfautolayout.com website to understand what's wrong, and it seems the issue is with UIView-Encapsulated-Layout-Width it thinks it's set to 0, but when I try to print view.frame.size.width I get correct width:
MWStepsActivityVC view width 375.0
TodayVC view width 375.0
Can someone help me with it? I'm struggling with this for a day already.
Finally I was able to fix it. I forgot to run configureContainerViews on main thread...
DispatchQueue.main.async {
self.configureContainerViews()
}
I am programmatically adding tableviewcell and its contents. It has 2 items in it. a label and an imageview. The thing is, I am trying to set the icon height and it breaks the constraints. It shows up fine in the simulator, but I am trying to figure out why it breaks the constraints. Here is the code:
override func viewDidLoad() {
super.viewDidLoad()
setTitle(title: menu.text)
tableView.register(ListMenuCell.self, forCellReuseIdentifier: "listMenuCell")
tableView.estimatedRowHeight = 150
tableView.rowHeight = UITableView.automaticDimension
setupTableView()
}
//MARK: Setting up tableview
func setupTableView() {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.white
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
}
class ListMenuCell: UITableViewCell {
//MARK: - Cell initializers
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented.")
}
//MARK: - Cell Properties
let menuLabel: UILabel = {
let label = UILabel()
label.text = ""
label.lineBreakMode = .byWordWrapping
label.numberOfLines = 0
label.font = UIFont.boldSystemFont(ofSize: 17)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let menuIcon: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
func setupViews() {
addSubview(menuLabel)
addSubview(menuIcon)
//Note: Don't have to change layoutMarginsGuide to safeAreaLayoutMarginsGuide because its already applied for the table view in which all the below subviews reside in.
menuIcon.layoutMarginsGuide.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor).isActive = true
menuIcon.layoutMarginsGuide.topAnchor.constraint(equalTo: self.layoutMarginsGuide.topAnchor, constant: 0).isActive = true
menuIcon.layoutMarginsGuide.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor, constant:0).isActive = true
menuIcon.layoutMarginsGuide.heightAnchor.constraint(equalToConstant: 40.0).isActive = true
menuIcon.layoutMarginsGuide.widthAnchor.constraint(equalTo: menuIcon.layoutMarginsGuide.heightAnchor).isActive = true
menuLabel.layoutMarginsGuide.leadingAnchor.constraint(equalTo: menuIcon.layoutMarginsGuide.trailingAnchor, constant: 20).isActive = true
menuLabel.layoutMarginsGuide.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
menuLabel.layoutMarginsGuide.topAnchor.constraint(equalTo: self.layoutMarginsGuide.topAnchor, constant: 0).isActive = true
menuLabel.layoutMarginsGuide.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor, constant:0).isActive = true
}
}
Here is the log for broken 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:0x600003790a50 UILayoutGuide:0x600002dcb3a0'UIViewLayoutMarginsGuide'.top == UILayoutGuide:0x600002dcb480'UIViewLayoutMarginsGuide'.top (active)>",
"<NSLayoutConstraint:0x600003790aa0 UILayoutGuide:0x600002dcb3a0'UIViewLayoutMarginsGuide'.bottom == UILayoutGuide:0x600002dcb480'UIViewLayoutMarginsGuide'.bottom (active)>",
"<NSLayoutConstraint:0x600003790b40 UILayoutGuide:0x600002dcb3a0'UIViewLayoutMarginsGuide'.height == 40 (active)>",
"<NSLayoutConstraint:0x600003790960 'UIView-bottomMargin-guide-constraint' V:[UILayoutGuide:0x600002dcb480'UIViewLayoutMarginsGuide']-(11)-| (active, names: '|':b2cEngageClient.ListMenuCell:0x7f9250667a40'listMenuCell' )>",
"<NSLayoutConstraint:0x600003791220 'UIView-Encapsulated-Layout-Height' b2cEngageClient.ListMenuCell:0x7f9250667a40'listMenuCell'.height == 62.5 (active)>",
"<NSLayoutConstraint:0x6000037908c0 'UIView-topMargin-guide-constraint' V:|-(11)-[UILayoutGuide:0x600002dcb480'UIViewLayoutMarginsGuide'] (active, names: '|':b2cEngageClient.ListMenuCell:0x7f9250667a40'listMenuCell' )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600003790b40 UILayoutGuide:0x600002dcb3a0'UIViewLayoutMarginsGuide'.height == 40 (active)>
remove this line
menuIcon.layoutMarginsGuide.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor, constant:0).isActive = true
and replace this
menuIcon.bottomAnchor.constraint(lessThanOrEqualTo: self.layoutMarginsGuide.bottomAnchor: -16).isActive = true
make sure you enabled the table automaticDimension
myTable.rowHeight = UITableView.automaticDimension
myTable.estimatedRowHeight = 100
This is the full code for cell
menuIcon.translatesAutoresizingMaskIntoConstraints = false
menuIcon.topAnchor.constraint(equalTo: topAnchor, constant: 16).isActive = true
menuIcon.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16).isActive = true
menuIcon.widthAnchor.constraint(equalToConstant: 40).isActive = true
menuIcon.heightAnchor.constraint(equalToConstant: 40).isActive = true
menuIcon.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor,constant: -16).isActive = true
menuLabel.translatesAutoresizingMaskIntoConstraints = false
menuLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16).isActive = true
menuLabel.leadingAnchor.constraint(equalTo: menuIcon.trailingAnchor, constant: 16).isActive = true
menuLabel.topAnchor.constraint(equalTo: topAnchor,constant: 16).isActive = true
menuLabel.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -16).isActive = true
I have an imageview with fixed height and width of 55 and a label under it but i get the following error. The constraints are not able to tell the tableview what the height should be even thought i set UITableView.automaticDimension
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:0x2811767b0 h=--& v=--& UILabel:0x1057292b0'steve_rodriquez'.minY == 0 (active, names: '|':UITableViewCellContentView:0x105729520 )>",
"<NSAutoresizingMaskLayoutConstraint:0x281176800 h=--& v=--& UILabel:0x1057292b0'steve_rodriquez'.height == 0 (active)>",
"<NSLayoutConstraint:0x2811765d0 UILabel:0x1057292b0'steve_rodriquez'.bottom == UITableViewCellContentView:0x105729520.bottom (active)>",
"<NSLayoutConstraint:0x2811772a0 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x105729520.height == 0.333333 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x2811765d0 UILabel:0x1057292b0'steve_rodriquez'.bottom == UITableViewCellContentView:0x105729520.bottom (active)>
My tableviewcell class is this with the constraints
class ChatTableViewCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(userImageView)
self.contentView.addSubview(username)
userImageView_constraints()
username_constraints()
}
var userImageView: UIImageView = {
let imageView = UIImageView()
let imageViewHeightAndWidth: CGFloat = 55
let image = UIImage(named: "steve")
imageView.image = image
imageView.clipsToBounds = true
imageView.layer.cornerRadius = imageViewHeightAndWidth / 2
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
var username: UILabel = {
let label = UILabel()
label.text = "steve_rodriquez"
label.textAlignment = .center
label.font = UIFont(name: "Arial", size: 13)
return label
}()
func userImageView_constraints(){
userImageView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 20).isActive = true
userImageView.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 0).isActive = true
userImageView.widthAnchor.constraint(equalToConstant: 55).isActive=true
userImageView.heightAnchor.constraint(equalToConstant: 55).isActive=true
}
func username_constraints(){
username.topAnchor.constraint(equalTo: userImageView.bottomAnchor, constant: 0).isActive = true
username.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: 0).isActive=true
username.centerXAnchor.constraint(equalTo: userImageView.centerXAnchor).isActive=true
username.widthAnchor.constraint(equalToConstant: 65).isActive=true
}
You forgot to set translatesAutoresizingMaskIntoConstraints = false on the label — as the error message quite plainly points out.
I've been trying to solve this problem for a few days now and I haven't been able to understand what is wrong with my constraints, and warnings keep popping up in the console.
Here are my UI elements:
let messageText : UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.backgroundColor = .clear
label.clipsToBounds = false
label.font = UIFont.systemFont(ofSize: 17)
return label
}()
private let messageCard : UIView = {
let card = UIView()
card.translatesAutoresizingMaskIntoConstraints = false
card.layer.cornerRadius = 16
card.layer.masksToBounds = true
card.clipsToBounds = false
return card
}()
private let avatar : CachedImageView = {
var imageView = CachedImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.heightAnchor.constraint(equalToConstant: 15).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 15).isActive = true
imageView.isAvatar = true
return imageView
}()
private let stackView : UIStackView = {
let stView = UIStackView()
stView.translatesAutoresizingMaskIntoConstraints = false
stView.backgroundColor = .clear
stView.axis = .vertical
return stView
}()
Here is my initialiser for the cell:
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.clipsToBounds = false
stackView.addArrangedSubview(messageText)
stackView.addArrangedSubview(avatar)
addSubview(messageCard)
addSubview(stackView)
let constraints = [
stackView.topAnchor.constraint(equalTo: topAnchor, constant: bubbleMargin + bubblePadding),
stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -(bubbleMargin + bubblePadding)),
stackView.widthAnchor.constraint(lessThanOrEqualToConstant: 300),
stackView.widthAnchor.constraint(greaterThanOrEqualToConstant: 30),
//stackView.heightAnchor.constraint(greaterThanOrEqualToConstant: 10), // ну хз
messageCard.topAnchor.constraint(equalTo: stackView.topAnchor, constant: -bubblePadding),
messageCard.bottomAnchor.constraint(equalTo: stackView.bottomAnchor, constant: bubblePadding),
messageCard.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: -bubblePadding),
messageCard.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: bubblePadding),
/*messageText.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 20),
messageText.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: -20),*/
/*avatar.heightAnchor.constraint(equalToConstant: 15),
avatar.widthAnchor.constraint(equalToConstant: 15),
avatar.centerXAnchor.constraint(equalTo: stackView.centerXAnchor),
avatar.centerYAnchor.constraint(equalTo: stackView.centerYAnchor),*/
]
leftConstr = stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: bubbleMargin + bubblePadding)
rightConstr = stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -(bubbleMargin + bubblePadding))
leftConstr.isActive = true
for c in constraints {
c.isActive = true
}
}
Here is the method that fills the message bubble:
func setMessage() {
messageText.text = messageWrapper.message.text
messageText.textColor = .white
messageCard.backgroundColor = messageWrapper.message.out == 1 ? .darkGray : .lightGray
leftConstr.isActive = messageWrapper.message.out == 0
rightConstr.isActive = messageWrapper.message.out == 1
avatar.isHidden = messageWrapper.message.out == 1
if messageWrapper.message.out == 0 {
if let photo = messageWrapper.group?.photo50 {
avatar.setSource(url: photo)
}
if let photo = messageWrapper.profile?.photo100 {
avatar.setSource(url: photo)
}
}
}
I've also noticed that the warnings really only appear when I start scrolling, but the warning suggests that there is something wrong with a constraints that the setMessage() method doesn't ever touch.
screenshot of the warning, since stack overflow really wasn't having it as a code snippet
Looks like your stack view with needs to be <= 300 points, but the stack view width must be equal to message cell width - 13 - 13. Since your message cell width is 414, and 414 - 13 - 13 = 388, and 388 is not <= 300, you get the error.
You should either remove the stack view's width constraint, or change either the leading or trailing constraint to be more flexible.
There are many solutions. For example, you could remove these two lines:
messageCard.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: -bubblePadding),
messageCard.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: bubblePadding),
and instead center the stack view within messageCard.
But ultimately you need to not give it conflicting constraints.