swift - UITableView 自定义单元格高度未正确管理 swift 中的动态约束
问题描述
我有表格视图并从xib中获取了自定义单元格,当有媒体或更大的文本时,我有很多东西要在单元格中展开,我已经在委托方法中以编程方式管理高度并在单元格中进行约束但有时当我打开应用程序并加载数据时,单元格会重叠,但是当我向上滚动时,它看起来很好。当我从 API 获取数据时,我可以做些什么来在重新加载后立即更新?
我认为代码没有问题,因为它在再次刷新或滚动表格视图后工作。高度委托方法如下:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let newsDict = self.tableDataNewsArray[indexPath.section]
let newsDataArray = newsDict["news"] as! [NewsBriefModel]
let newsObject = newsDataArray[indexPath.item]
if (self.moreExpandableArray.firstIndex(of: newsObject.newsbrief_id) != nil) {
return 62 + 58 + 4 + 62 //(62:// label minimum height )(46: other than label caponents in cell :: 4: spacing
// 62: more/less button view height)
}else if (self.lessExpandableArray.firstIndex(of: newsObject.newsbrief_id) != nil) {
var baseMoreHeight : CGFloat = newsObject.labelHeightForPostText + 58 + 4 + 62 //(46: other than label caponents in cell :: 4: spacing
// 62: more/less button view height)
if newsObject.numberOfImagesInPost > 0 {
baseMoreHeight = baseMoreHeight + 110
}
if newsObject.numberOfDocumentInPost > 0 {
baseMoreHeight = baseMoreHeight + CGFloat((newsObject.numberOfDocumentInPost * 45))
}
if newsObject.numberOfVideosInPost > 0 {
baseMoreHeight = baseMoreHeight + CGFloat((newsObject.numberOfVideosInPost * 100))
}
return baseMoreHeight
}
}
我在 cellForRow 中调用以更改 UI 的单元格方法:
// MARK: CELL METHODS
func showNormalView() {
self.postTitleLabel.numberOfLines = 3
self.postTitleTextView.textContainer.maximumNumberOfLines = 3
self.postTitleTextView.textContainer.lineBreakMode = .byTruncatingTail
self.mediaViewHeight.constant = 0
self.documentTableViewHeight.constant = 0
self.videoTableViewHeight.constant = 0
self.multiMediaView.isHidden = true
self.loadImages(urlString: [])
self.updateConstraintsIfNeeded()
self.setNeedsLayout()
self.layoutIfNeeded()
}
func showMoreView() {
self.postTitleLabel.numberOfLines = 3
self.postTitleTextView.textContainer.maximumNumberOfLines = 3
self.postTitleTextView.textContainer.lineBreakMode = .byTruncatingTail
self.mediaViewHeight.constant = 50
self.imageGridViewHeight.constant = 0
self.documentTableViewHeight.constant = 0
self.videoTableViewHeight.constant = 0
self.loadImages(urlString: [])
self.multiMediaView.isHidden = false
self.updateConstraintsIfNeeded()
self.setNeedsLayout()
self.layoutIfNeeded()
}
解决方案
我建议使用 UITableView.automaticDimension 而不是手动计算高度,并使用委托模式来更新单元格的高度。
这是我用这种方法得到的:
和代码示例:
import UIKit
struct Content {
let author: String
let date: String
let views: UInt
let recs: UInt
let content: String
}
let longText = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
"""
protocol MyCellDelegate: AnyObject {
func contentDidChange(cell: UITableViewCell)
}
final class MyCell: UITableViewCell {
static let reuseIdentifier = "MyCell"
weak var delegate: MyCellDelegate?
private var viewsCountLabel: UILabel!
private var recsLabel: UILabel!
private var authorLabel: UILabel!
private var dateLabel: UILabel!
private var contentLabel: UILabel!
private var toggleHeightButton: UIButton!
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupSubviews()
}
required init?(coder: NSCoder) {
fatalError("not implemented")
}
override func prepareForReuse() {
super.prepareForReuse()
viewsCountLabel.text = nil
recsLabel.text = nil
authorLabel.text = nil
dateLabel.text = nil
contentLabel.text = nil
contentLabel.numberOfLines = 2
}
public func setContent(_ content: Content) {
viewsCountLabel.text = "Views: \(content.views)"
recsLabel.text = "Recs: \(content.recs)"
authorLabel.text = content.author
dateLabel.text = content.date
contentLabel.text = content.content
}
private func setupSubviews() {
let leftStackView = createLeftStack()
let rightStackView = createRightStack()
let stackView = UIStackView(arrangedSubviews: [leftStackView, rightStackView])
stackView.axis = .horizontal
stackView.spacing = 5
stackView.alignment = .top
contentView.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 3),
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -3)
])
}
private func createLeftStack() -> UIStackView {
let iconView = createIconView()
viewsCountLabel = createLabel()
recsLabel = createLabel()
let stackView = UIStackView(arrangedSubviews: [iconView, viewsCountLabel, recsLabel])
stackView.axis = .vertical
stackView.setCustomSpacing(4, after: iconView)
stackView.alignment = .leading
return stackView
}
private func createRightStack() -> UIStackView {
authorLabel = createLabel()
dateLabel = createLabel()
contentLabel = UILabel()
contentLabel.font = UIFont.systemFont(ofSize: 13, weight: .bold)
contentLabel.contentMode = .topLeft
toggleHeightButton = UIButton(type: .system)
toggleHeightButton.setTitle("Toggle", for: .normal)
toggleHeightButton.addTarget(self, action: #selector(expandDidTap), for: .touchUpInside)
toggleHeightButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
let stackView = UIStackView(arrangedSubviews: [authorLabel, dateLabel, contentLabel, toggleHeightButton])
stackView.axis = .vertical
stackView.alignment = .leading
return stackView
}
private func createIconView() -> UIView {
let iconView = UIView()
iconView.backgroundColor = .blue
iconView.layer.cornerRadius = 4
iconView.layer.masksToBounds = true
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 8)
label.numberOfLines = 2
label.text = "Upload Icon"
label.textAlignment = .center
label.textColor = .white
iconView.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
iconView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: iconView.leadingAnchor, constant: 3),
label.trailingAnchor.constraint(equalTo: iconView.trailingAnchor, constant: -3),
label.topAnchor.constraint(equalTo: iconView.topAnchor, constant: 3),
label.bottomAnchor.constraint(equalTo: iconView.bottomAnchor, constant: -3),
iconView.heightAnchor.constraint(equalToConstant: 40),
iconView.widthAnchor.constraint(equalToConstant: 40)
])
return iconView
}
private func createLabel() -> UILabel {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 8, weight: .light)
label.numberOfLines = 1
return label
}
@objc func expandDidTap() {
let isExpand = contentLabel.numberOfLines != 0
if isExpand {
contentLabel.numberOfLines = 0
} else {
contentLabel.numberOfLines = 2
}
delegate?.contentDidChange(cell: self)
}
}
final class MyViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
private var tableView: UITableView {
view as! UITableView
}
private var data: [Content] = [] {
didSet {
tableView.reloadData()
}
}
override func loadView() {
let tableView = UITableView()
tableView.delegate = self
tableView.dataSource = self
tableView.register(MyCell.self, forCellReuseIdentifier: MyCell.reuseIdentifier)
view = tableView
}
override func viewDidLoad() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.data = [
Content(author: "Author1", date: "15:07", views: 2, recs: 1, content: longText),
Content(author: "Author2", date: "15:07", views: 2, recs: 1, content: longText),
Content(author: "Author3", date: "15:07", views: 2, recs: 1, content: longText),
Content(author: "Author1", date: "15:07", views: 2, recs: 1, content: longText)
]
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MyCell.reuseIdentifier, for: indexPath) as! MyCell
cell.delegate = self
let content = data[indexPath.row]
cell.setContent(content)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
extension MyViewController: MyCellDelegate {
func contentDidChange(cell: UITableViewCell) {
UIView.animate(withDuration: 0.2) {
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
}
}
推荐阅读
- azure - 无法在我的计算机上设置 azure data factory V2 自托管集成运行时
- ruby-on-rails - 当您使用 ActiveStorage 上传多个文件时,是否可以确定刚刚上传的内容?
- javascript - Rails 6 - Javascript:未捕获的 ReferenceError:未定义 Quill
- python - 如何在pytorch中进行批量填充
- delphi - delphi 从 TMemorystream 读取而不改变位置
- api - 我可以创建一个应用程序来询问用户的 gdrive id 和密码,然后自动将文件从本地计算机上传到 gdrive 吗?
- pyspark - 如何捕获未由 pyspark 函数转换的行?
- android - 等待进程出现在设备上超时
- c++ - 错误 C2678:二进制“>>”:未找到采用“std::ifstream”类型的左侧操作数的运算符
- android - CastListner 在片段中不起作用