Reputation: 1465
I'm creating a custom UITableViewCell
. For now, all I want is for it to have a UIButton
on the left (checkButton
), and two UILabel
s (titleLabel
and notesLabel
) to the right of the button.
Basically, it should look like a standard UITableViewCell
with an image and two text labels (but please don't tell me to just re-use a standard cell, because I can't do this for a variety of reasons). The button should have a fixed size (16x16) and be vertically centered in the cell. The two labels should line wrap and expand to fit their content. I'm trying to define this cell programmatically, so I've created the below initializer to define the constraints.
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.font = UIFont.systemFont(ofSize: 16)
titleLabel.lineBreakMode = .byWordWrapping
titleLabel.numberOfLines = 0
contentView.addSubview(titleLabel)
checkButton.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(checkButton)
notesLabel.translatesAutoresizingMaskIntoConstraints = false
notesLabel.font = UIFont.systemFont(ofSize: 13)
notesLabel.lineBreakMode = .byWordWrapping
notesLabel.numberOfLines = 0
contentView.addSubview(notesLabel)
addConstraint(NSLayoutConstraint(item: titleLabel,
attribute: .top,
relatedBy: .equal,
toItem: contentView,
attribute: .top,
multiplier: 1,
constant: 0))
addConstraint(NSLayoutConstraint(item: notesLabel,
attribute: .top,
relatedBy: .equal,
toItem: titleLabel,
attribute: .bottom,
multiplier: 1,
constant: 0))
addConstraint(NSLayoutConstraint(item: titleLabel,
attribute: .trailing,
relatedBy: .equal,
toItem: contentView,
attribute: .trailing,
multiplier: 1,
constant: -10))
addConstraint(NSLayoutConstraint(item: notesLabel,
attribute: .trailing,
relatedBy: .equal,
toItem: contentView,
attribute: .trailing,
multiplier: 1,
constant: -10))
addConstraint(NSLayoutConstraint(item: notesLabel,
attribute: .bottom,
relatedBy: .equal,
toItem: contentView,
attribute: .bottom,
multiplier: 1,
constant: 0))
addConstraint(NSLayoutConstraint(item: checkButton,
attribute: .leading,
relatedBy: .equal,
toItem: contentView,
attribute: .leading,
multiplier: 1,
constant: 20))
addConstraint(NSLayoutConstraint(item: checkButton,
attribute: .centerY,
relatedBy: .equal,
toItem: contentView,
attribute: .centerY,
multiplier: 1,
constant: 0))
addConstraint(NSLayoutConstraint(item: checkButton,
attribute: .height,
relatedBy: .equal,
toItem: nil,
attribute: .notAnAttribute,
multiplier: 0,
constant: 16))
addConstraint(NSLayoutConstraint(item: checkButton,
attribute: .width,
relatedBy: .equal,
toItem: nil,
attribute: .notAnAttribute,
multiplier: 0,
constant: 16))
addConstraint(NSLayoutConstraint(item: notesLabel,
attribute: .leading,
relatedBy: .equal,
toItem: checkButton,
attribute: .trailing,
multiplier: 1,
constant: 12))
addConstraint(NSLayoutConstraint(item: titleLabel,
attribute: .leading,
relatedBy: .equal,
toItem: checkButton,
attribute: .trailing,
multiplier: 1,
constant: 12))
}
When I run this code, it works mostly as expected, except that Xcode prints the following warning: [Warning] Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tableview cell's content view. We're considering the collapse unintentional and using standard height instead.
I'd normally just ignore this, but it seems to be preventing the cell from expanding to fit its content. For example, if one of the labels has enough content to expand to 3 lines, only the first line appears. The behavior I want is for the labels (and by extension, the cell) to expand to fit their content. What am I doing wrong with the height constraints?
Upvotes: 0
Views: 95
Reputation: 12208
Here is my approach
private func setUp() {
self.selectionStyle = .none
self.contentView.backgroundColor = .white
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
button.setImage(UIImage(named: "arrow"), for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(button)
button.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 8.0).isActive = true
button.centerYAnchor.constraint(equalTo: self.contentView.centerYAnchor).isActive = true
button.widthAnchor.constraint(equalToConstant: button.frame.width).isActive = true
button.heightAnchor.constraint(equalToConstant: button.frame.height).isActive = true
let view = UIView()
view.backgroundColor = .red
view.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(view)
view.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 8.0).isActive = true
view.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: button.frame.width + 16.0).isActive = true
view.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: 8.0).isActive = true
view.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -8.0).isActive = true
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.numberOfLines = 0
view.addSubview(titleLabel)
titleLabel.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
titleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
notesLabel.translatesAutoresizingMaskIntoConstraints = false
notesLabel.numberOfLines = 0
view.addSubview(notesLabel)
notesLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8.0).isActive = true
notesLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
notesLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
notesLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
If the labels are multiline dynamic height, Use following to enable Self-Sizing cells
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 44.0
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
That yields
Upvotes: 0
Reputation: 2805
NOTE: Please add your Label's and Button's in Cell not in contentView of the cell. below code will work, please check and let me know incase of any issue.
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(titleLabel)
addSubview(checkButton)
addSubview(notesLabel)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.font = UIFont.systemFont(ofSize: 16)
titleLabel.lineBreakMode = .byWordWrapping
titleLabel.numberOfLines = 0
checkButton.translatesAutoresizingMaskIntoConstraints = false
notesLabel.translatesAutoresizingMaskIntoConstraints = false
notesLabel.font = UIFont.systemFont(ofSize: 13)
notesLabel.lineBreakMode = .byWordWrapping
notesLabel.numberOfLines = 0
addConstraint(NSLayoutConstraint(item: titleLabel,
attribute: .top,
relatedBy: .equal,
toItem: self,
attribute: .top,
multiplier: 1,
constant: 0))
addConstraint(NSLayoutConstraint(item: notesLabel,
attribute: .top,
relatedBy: .equal,
toItem: titleLabel,
attribute: .bottom,
multiplier: 1,
constant: 0))
addConstraint(NSLayoutConstraint(item: titleLabel,
attribute: .trailing,
relatedBy: .equal,
toItem: self,
attribute: .trailing,
multiplier: 1,
constant: -10))
addConstraint(NSLayoutConstraint(item: notesLabel,
attribute: .trailing,
relatedBy: .equal,
toItem: self,
attribute: .trailing,
multiplier: 1,
constant: -10))
addConstraint(NSLayoutConstraint(item: notesLabel,
attribute: .bottom,
relatedBy: .equal,
toItem: self,
attribute: .bottom,
multiplier: 1,
constant: 0))
addConstraint(NSLayoutConstraint(item: checkButton,
attribute: .leading,
relatedBy: .equal,
toItem: self,
attribute: .leading,
multiplier: 1,
constant: 20))
addConstraint(NSLayoutConstraint(item: checkButton,
attribute: .centerY,
relatedBy: .equal,
toItem: self,
attribute: .centerY,
multiplier: 1,
constant: 0))
addConstraint(NSLayoutConstraint(item: checkButton,
attribute: .height,
relatedBy: .equal,
toItem: nil,
attribute: .notAnAttribute,
multiplier: 0,
constant: 16))
addConstraint(NSLayoutConstraint(item: checkButton,
attribute: .width,
relatedBy: .equal,
toItem: nil,
attribute: .notAnAttribute,
multiplier: 0,
constant: 16))
addConstraint(NSLayoutConstraint(item: notesLabel,
attribute: .leading,
relatedBy: .equal,
toItem: checkButton,
attribute: .trailing,
multiplier: 1,
constant: 12))
addConstraint(NSLayoutConstraint(item: titleLabel,
attribute: .leading,
relatedBy: .equal,
toItem: checkButton,
attribute: .trailing,
multiplier: 1,
constant: 12))
}
Upvotes: 1