I have dynamically changing tableview cells that have two labels on top of each other. The top label always has some text, but the bottom label sometimes does not. Upon loading the tableview, the cells without text for the bottom label disappear entirely with only their header showing. If I scroll down and come back up, these previously missing cells appear correctly.
I think that this has something to do with my constraints, but when I try to adjust the constraints, I've only made things worse.
How do I make the constraints such that the cells will always appear at first upon loading the screen, including when the bottom label has no text?
Here's the code for the tableview cell:
class TransactionHistoryTableViewCell: UITableViewCell {
private var itemView = UIView()
private var itemNameLabel = UILabel()
private var sizeLabel = UILabel()
func setup(_ lineItem: MenuItem) {
contentView.backgroundColor = .white
configureItemNameLabel(lineItem)
configureSizeLabel(lineItem)
configureTransactionView(lineItem)
}
private func configureTransactionView(_ lineItem: MenuItem) {
itemView.clipsToBounds = true
itemView.layer.cornerRadius = 5
itemView.layer.borderColor = UIColor.lightGray.cgColor
itemView.layer.borderWidth = 0.5
itemView.backgroundColor = .white
contentView.addSubview(itemView)
itemView.translatesAutoresizingMaskIntoConstraints = false
itemView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
itemView.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.8).isActive = true
itemView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 4).isActive = true
itemView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -4).isActive = true
}
private func returnText(_ menuItem: MenuItem) -> String {
var text = ""
guard let modifiers = menuItem.modifiers else { print("modifiers are nil"); return "" }
if modifiers.isEmpty && menuItem.quantity == 1 {
text = ""
} else if !modifiers.isEmpty && menuItem.quantity == 1 {
text = generateModifierText(menuItem)
} else if !modifiers.isEmpty && menuItem.quantity > 1 {
let theText = generateModifierText(menuItem)
text = "\(theText); Quantity: \(menuItem.quantity)"
} else {
text = "Quantity: \(menuItem.quantity)"
}
return text
}
private func configureItemNameLabel(_ lineItem: MenuItem) {
itemNameLabel.text = lineItem.name
let fontSize = getSize(large: 14, medium: 13.5, small: 12)
itemNameLabel.font = UIFont(name: AppFont.secondary.name, size: fontSize)
itemNameLabel.numberOfLines = 0
itemView.addSubview(itemNameLabel)
itemNameLabel.translatesAutoresizingMaskIntoConstraints = false
itemNameLabel.leftAnchor.constraint(equalTo: itemView.leftAnchor, constant: 15).isActive = true
let verticalOffset = getSize(large: 10, medium: 7, small: 5)
itemNameLabel.topAnchor.constraint(equalTo: itemView.topAnchor, constant: verticalOffset).isActive = true
itemNameLabel.widthAnchor.constraint(equalToConstant: 195).isActive = true
itemView.addSubview(sizeLabel)
itemNameLabel.bottomAnchor.constraint(equalTo: sizeLabel.topAnchor, constant: -3).isActive = true
}
func heightForView(text:String, font:UIFont, width:CGFloat) -> CGFloat{
let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = text
label.sizeToFit()
return label.frame.height
}
override func prepareForReuse() {
super.prepareForReuse()
itemNameLabel.text = ""
sizeLabel.text = ""
}
private func configureSizeLabel(_ menuItem: MenuItem) {
guard let modifiers = menuItem.modifiers else { print("modifiers are nil"); return }
if modifiers.isEmpty && menuItem.quantity == 1 {
sizeLabel.text = ""
} else if !modifiers.isEmpty && menuItem.quantity == 1 {
sizeLabel.text = generateModifierText(menuItem)
} else if !modifiers.isEmpty && menuItem.quantity > 1 {
let text = generateModifierText(menuItem)
if text != "" {
sizeLabel.text = "\(text); Quantity: \(menuItem.quantity)"
} else {
sizeLabel.text = "Quantity: \(menuItem.quantity)"
}
} else {
sizeLabel.text = "Quantity: \(menuItem.quantity)"
}
sizeLabel.backgroundColor = .white
sizeLabel.textColor = .gray
sizeLabel.numberOfLines = 0
let fontSize = getSize(large: 11, medium: 10.5, small: 9.5)
sizeLabel.font = UIFont(name: AppFont.secondary.name, size: fontSize)
sizeLabel.translatesAutoresizingMaskIntoConstraints = false
sizeLabel.topAnchor.constraint(equalTo: itemNameLabel.bottomAnchor, constant: 3).isActive = true
sizeLabel.leftAnchor.constraint(equalTo: itemNameLabel.leftAnchor).isActive = true
sizeLabel.widthAnchor.constraint(equalToConstant: 200).isActive = true
sizeLabel.bottomAnchor.constraint(equalTo: itemView.bottomAnchor, constant: -3).isActive = true
}
private func generateModifierText(_ menuItem: MenuItem) -> String {
var text = ""
guard let modifiers = menuItem.modifiers else { return "" }
var optionNames = [String]()
for modifier in modifiers {
if !modifier.options.isEmpty {
for options in modifier.options{
if options.name.uppercased() != "NONE" {
optionNames.append(options.name)
}
}
}
}
for x in 0..<optionNames.count {
if x != optionNames.count - 1 {
text += "\(optionNames[x]), "
} else {
text += "\(optionNames[x])"
}
}
return text
}
private func generateSizeLabelFontSize() -> CGFloat {
return getSize(large: 11, medium: 10, small: 9.5)
}
}
Code from the view controller:
override func viewDidLoad() {
super.viewDidLoad()
configureNavBar()
configureTableView()
getOrders()
NotificationCenter.default.addObserver(self, selector: #selector(popToRootVC), name: NSNotification.Name(rawValue: "PopToRootVCFromSettingsVC"), object: nil)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "transactionHistoryCell", for: indexPath) as? TransactionHistoryTableViewCell else { getOrders(); return UITableViewCell() }
if let lineItem = orders[indexPath.section].lineItems?[indexPath.row] {
cell.setup(lineItem)
}
return cell
}
private func getOrders() {
let service = TransactionService()
service.getTransactionData(completion: { (orders) in
guard let orders = orders else { self.handleNoOrderHistory(); return }
let filteredOrders = orders.filter{ $0.status == "NEW" || $0.status == "IN_PROGRESS" || $0.status == "READY" || $0.status == "COMPLETE" }
if filteredOrders.isEmpty {
DispatchQueue.main.async {
self.handleNoOrderHistory()
return
}
} else {
DispatchQueue.main.async {
self.orders = filteredOrders.sorted{ $0.date > $1.date }
self.noOrdersView.isHidden = true
self.tableView.reloadData()
}
}
})
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 85
}
The compiler gives warnings like this one:
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:0x600001e972a0 h=--& v=--& UILabel:0x7f9a0050b780.midY == 0 (active),
NSAutoresizingMaskLayoutConstraint:0x600001e97610 h=--& v=--& UILabel:0x7f9a0050b780.height == 0 (active),
NSLayoutConstraint:0x600001e8bca0 V:|-(5)-[UILabel:0x7f9a0050b490'5\" Focaccia Everything'] (active, names: '|':UIView:0x7f9a0050b2b0 ),
NSLayoutConstraint:0x600001d63660 UILabel:0x7f9a0050b490'5\" Focaccia Everything'.bottom == UILabel:0x7f9a0050b780.top - 3 (active)
)
Will attempt to recover by breaking constraint
NSLayoutConstraint:0x600001d63660 UILabel:0x7f9a0050b490'5" Focaccia Everything'.bottom == UILabel:0x7f9a0050b780.top - 3 (active)
The problem is that before you add any subview and apply constraints, you need to set its
translatesAutoresizing...
tofalse
.So, you call
configureItemNameLabel
and add thesizeLabel
, before you callconfigureSizeLabel
and fix itstranslatesAutoresizing...
.