UITableViewCell 使用自定义 layoutSubviews() 出现在屏幕上后隐藏部分内容
UITableViewCell hides part of content after appearing on the screen with custom layoutSubviews()
我正在为 UITableView 苦苦挣扎。正如您在 table 视图第三部分的 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
单元格的 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()
的时间。针对在不同线程中发现的类似问题,我尝试了许多解决方案,但其中 none 对我有所帮助。 tableView(_:heightForRowAt:)
导致第三个单元格正确显示,但第一个单元格的底角是圆角的。而且它们的高度都是固定的,这是不可能发生的。
但真正奇怪的是:当我在出列单元格中打印 isLastInSection
时,它意外地被舍入了调试器 returns 我是 false:
(lldb) po indexPath.row == (question.answers?.count ?? 1) - 1
false
正如您在 Debug View Hierarchy 中看到的那样,视图文本存在,这就是我将问题定义为隐藏部分内容的原因。
您将单元格出列,并且每次添加子视图时都不会检查它们是否已经存在,这在回收单元格的情况下会发生。这可能会破坏约束并导致大小调整不正确。
同样的舍入问题 - 您设置了圆角,但当重复使用的单元格不应被舍入时,您永远不会恢复此行为。
解决此问题的最佳方法是添加额外的检查并仅创建一次子视图:
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()
}
我正在为 UITableView 苦苦挣扎。正如您在 table 视图第三部分的 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
单元格的 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()
的时间。针对在不同线程中发现的类似问题,我尝试了许多解决方案,但其中 none 对我有所帮助。 tableView(_:heightForRowAt:)
导致第三个单元格正确显示,但第一个单元格的底角是圆角的。而且它们的高度都是固定的,这是不可能发生的。
但真正奇怪的是:当我在出列单元格中打印 isLastInSection
时,它意外地被舍入了调试器 returns 我是 false:
(lldb) po indexPath.row == (question.answers?.count ?? 1) - 1
false
正如您在 Debug View Hierarchy 中看到的那样,视图文本存在,这就是我将问题定义为隐藏部分内容的原因。
您将单元格出列,并且每次添加子视图时都不会检查它们是否已经存在,这在回收单元格的情况下会发生。这可能会破坏约束并导致大小调整不正确。
同样的舍入问题 - 您设置了圆角,但当重复使用的单元格不应被舍入时,您永远不会恢复此行为。
解决此问题的最佳方法是添加额外的检查并仅创建一次子视图:
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()
}