UITableViewCell 使用自定义 layoutSubviews() 出现在屏幕上后隐藏部分内容
Posted
技术标签:
【中文标题】UITableViewCell 使用自定义 layoutSubviews() 出现在屏幕上后隐藏部分内容【英文标题】:UITableViewCell hides part of content after appearing on the screen with custom layoutSubviews() 【发布时间】:2019-03-15 22:55:10 【问题描述】:我在 UITableView 上苦苦挣扎。正如您在表格视图第三部分的this video 中看到的那样,第三个单元格未正确显示。当我像这样将我的单元格出列时会发生这种情况:
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 的setup()
方法:
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
变量在layoutSubviews()
内部使用:
override func layoutSubviews()
super.layoutSubviews()
if isLastInSection
roundCorners(corners: [.bottomLeft, .bottomRight], radius: 16.0)
contentView.layoutIfNeeded()
最后是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
当我将 isLastInSection
设置为 false 的单元格出列时,单元格将按预期显示 (related video)。所以我认为问题在于细胞的生命周期以及何时调用layoutSubview()
。对于在不同线程中发现的类似问题,我尝试了许多解决方案,但没有一个对我有帮助。 tableView(_:heightForRowAt:)
使第三个单元格正确显示,但第一个单元格的底角为圆角。而且它们的高度都是固定的,这是不可能发生的。
但真正奇怪的是:当我在出队单元格期间打印 isLastInSection
时,意外地被舍入调试器返回错误:
(lldb) po indexPath.row == (question.answers?.count ?? 1) - 1
false
正如您在 Debug View Hierarchy 中看到的那样,存在视图文本,这就是我将问题定义为隐藏部分内容的原因。
【问题讨论】:
您将单元格出列并且每次添加子视图等时都不会检查它是否已经存在,这在回收单元格的情况下会发生。这可能会打破约束或某事。舍入的问题相同 - 您设置了圆角,但如果重复使用的单元格不应被舍入,则您永远不会恢复此行为。尝试不重复使用单元格(每次只创建一个新单元格)并检查它是否有帮助。 @WojciechKulik 创建单元而不重用解决了我的问题。但是表格视图现在可能会滞后。当然,我理解为什么单元格不四舍五入就不会回到正常框架。我不明白为什么当isLastInSection
参数设置为false 时单元格有圆角。单元格的框架和子视图也是如此。每次出队时我都会设置视图,但约束等是相同的。
我已经将子视图添加到单元格的 init 部分,但问题仍然存在,所以这是由于 layoutSubviews()
中的圆角引起的
【参考方案1】:
您使单元格出列,并且每次添加子视图时,您都不会检查它们是否已经存在,这在回收单元格的情况下会发生。这可能会破坏约束并导致大小不正确。
圆角也有同样的问题 - 你设置了圆角,但是当重复使用的单元格不应该被圆角时,你永远不会恢复这种行为。
解决此问题的最佳方法是添加额外的检查并仅创建一次子视图:
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
或者,您可以保留一些属性,例如isInitialized
,并在开始时检查。
您的方法layoutSubviews
也必须支持这两种情况:
override func layoutSubviews()
super.layoutSubviews()
if isLastInSection
roundCorners(corners: [.bottomLeft, .bottomRight], radius: 16.0)
else
layer.mask = nil
contentView.layoutIfNeeded()
【讨论】:
是的!就是这样!我必须从图层中删除蒙版。我欠你一个人情!另外我只想补充一点,我不必检查self.subviews.count == 0
,因为现在所有子视图都被添加到 init 部分:)以上是关于UITableViewCell 使用自定义 layoutSubviews() 出现在屏幕上后隐藏部分内容的主要内容,如果未能解决你的问题,请参考以下文章