UITableView 自定义单元格高度未正确管理 swift 中的动态约束
Posted
技术标签:
【中文标题】UITableView 自定义单元格高度未正确管理 swift 中的动态约束【英文标题】:UITableView custom cell height is not properly managing for dynamic constraints in swift 【发布时间】:2020-01-29 10:27:57 【问题描述】:我有表格视图并从 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 的 Cell 方法:
// 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()
【问题讨论】:
您应该在 Xcode 中调试视图层次结构,以检查unwanted
显示的视图。
我认为单元格约束不合适。
@Paresh.P 如果是这样的话,那么它可能一直都有问题,这个问题只是第一次......在我再次滚动或刷新之后它会正确显示
@AndreasOetjen 是的,我试过的事情是 tableview 单元格高度没有增加。
你能用cellforrow代码和数据源描述更多吗?
【参考方案1】:
我建议使用 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()
【讨论】:
【参考方案2】:我认为最好使用UITableView.automaticDimension
,并确保设置estimatedRowHeight
,除此之外,确保单元格中的最后一个元素与ContentView
的ContentView
具有底部约束@
【讨论】:
以上是关于UITableView 自定义单元格高度未正确管理 swift 中的动态约束的主要内容,如果未能解决你的问题,请参考以下文章
使用 UITableView 的可变高度自定义单元格调用 reloadData 时保持 UITableView 的当前位置
自定义 UITableView 单元格图像转换未应用于可重用单元格