I'm struggling with UITableView. As you can see in this video in third section of table view third cell isn't display correctly. That happens when I dequeue my cell like that:
let cell = tableView.dequeueReusableCell(withIdentifier: MultipleSelectAnswerSurveyTableViewCellIdentifier, for: indexPath) as! MultipleSelectAnswerSurveyTableViewCell
cell.setup(answer: question.answers?[indexPath.row].value ?? "", isSelected: false, style: style, isLastInSection: indexPath.row == (question.answers?.count ?? 1) - 1)
return cell
Cell's setup()
method:
func setup(answer: String, isSelected: Bool, style: Style, isLastInSection: Bool) {
self.isLastInSection = isLastInSection
selectionStyle = .none
backgroundColor = style.survey.singleSelectAnswerTableViewCell.backgroundColor
answerLabel.textColor = style.survey.singleSelectAnswerTableViewCell.answerLabelColor
answerLabel.font = style.survey.singleSelectAnswerTableViewCell.answerLabelFont
answerLabel.text = answer
addSubview(answerLabel)
addSubview(selectionIndicator)
answerLabel.snp.makeConstraints { make in
make.left.equalTo(8)
make.centerY.equalTo(selectionIndicator.snp.centerY)
make.top.equalTo(8)
make.bottom.equalTo(-8)
make.right.equalTo(selectionIndicator.snp.left).offset(-8)
}
selectionIndicator.snp.makeConstraints { make in
make.right.equalTo(-8)
make.top.greaterThanOrEqualTo(8)
make.bottom.lessThanOrEqualTo(-8)
make.width.height.equalTo(26)
}
}
self.isLastInSection
variable is used inside layoutSubviews()
:
override func layoutSubviews() {
super.layoutSubviews()
if isLastInSection {
roundCorners(corners: [.bottomLeft, .bottomRight], radius: 16.0)
}
contentView.layoutIfNeeded()
}
And finally roundCorners()
:
extension UIView {
func roundCorners(corners: UIRectCorner, radius: CGFloat) {
let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.path = path.cgPath
layer.mask = mask
}
}
When I dequeue cell with isLastInSection
set to false cell is being displayed as expected (related video). So I think the problem is in life cycle of the cell and when the layoutSubview()
is being called. I tried many solutions for similar problem found in different threads but none of them helped me. tableView(_:heightForRowAt:)
causes the third cell to display correctly, but the first one has rounded bottom corners. Also all of them are fixed in height and that cannot happen.
But what is really weird: when I print the isLastInSection
during dequeueing cell which is unexpectedly rounded debugger returns me false:
(lldb) po indexPath.row == (question.answers?.count ?? 1) - 1
false
As you can see in Debug View Hierarchy view text exists so that's why I 've defined the problem as hiding part of content.
You dequeue cell and each time you add subviews you don't check if they are already there which will happen in case of recycled cell. That probably breaks constraints and causes incorrect sizing.
Same problem with rounding - you set rounded corners, but you never revert this behavior when reused cell should not be rounded.
Best way to solve this issue would be to add additional check and create subviews only once:
func setup(answer: String, isSelected: Bool, style: Style, isLastInSection: Bool) {
if self.subviews.count == 0 {
// adding subviews etc.
// code that needs to be performed only once for whole cell's life
}
self.isLastInSection = isLastInSection
// set up content that changes for each cell (like text)
// for example a code depending on parameters of this method
}
alternatively you could keep some property like isInitialized
and check that at the beginning.
Also your method layoutSubviews
must support both cases:
override func layoutSubviews() {
super.layoutSubviews()
if isLastInSection {
roundCorners(corners: [.bottomLeft, .bottomRight], radius: 16.0)
} else {
layer.mask = nil
}
contentView.layoutIfNeeded()
}