具有动态高度的 UITableViewCell 内的 UITableView
Posted
技术标签:
【中文标题】具有动态高度的 UITableViewCell 内的 UITableView【英文标题】:UITableView inside UITableViewCell with dynamic height 【发布时间】:2017-09-27 07:18:05 【问题描述】:我需要将 UITableView 放在具有自动布局的 UITableViewCell 中,因为第二个表有不同的行数,而一行可以有不同的高度。 这是我的视图控制器
class ViewController: UIViewController
let tableView = UITableView()
let cellId = "firstTableCellId"
override func viewDidLoad()
super.viewDidLoad()
setupView()
tableView.reloadData()
view.backgroundColor = UIColor.gray
func setupView()
tableView.delegate = self
tableView.dataSource = self
tableView.separatorStyle = .none
tableView.register(NextTable.self, forCellReuseIdentifier: cellId)
tableView.backgroundColor = UIColor.green
tableView.separatorStyle = .singleLine
view.addSubview(tableView)
view.addConstraintsWithFormat("V:|-60-[v0]-5-|", views: tableView)
view.addConstraintsWithFormat("H:|-8-[v0]-8-|", views: tableView)
extension ViewController: UITableViewDelegate
extension ViewController: UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return 2
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
return UITableViewAutomaticDimension
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat
return 80
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! NextTable
cell.layoutIfNeeded()
return cell
NextTable 是第一个表中的单元格
class NextTable: UITableViewCell
var myTableView: UITableView!
let cellId = "nextTableCellId"
override init(style: UITableViewCellStyle, reuseIdentifier: String?)
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = UIColor.brown
setupView()
myTableView.reloadData()
required init?(coder aDecoder: NSCoder)
super.init(coder: aDecoder)
let label: UILabel =
let label = UILabel()
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.text = "Next table:"
label.textColor = UIColor.black
label.sizeToFit()
label.backgroundColor = UIColor.cyan
return label
()
func setupView()
myTableView = UITableView()
myTableView.delegate = self
myTableView.dataSource = self
myTableView.separatorStyle = .singleLineEtched
myTableView.backgroundColor = UIColor.blue
myTableView.register(TableCell.self, forCellReuseIdentifier: cellId)
myTableView.isScrollEnabled = false
addSubview(myTableView)
addSubview(label)
addConstraintsWithFormat("H:|-30-[v0]-30-|", views: myTableView)
addConstraintsWithFormat("H:|-30-[v0]-30-|", views: label)
addConstraint(NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 15))
addConstraint(NSLayoutConstraint(item: myTableView, attribute: .top, relatedBy: .equal, toItem: label, attribute: .bottom, multiplier: 1.0, constant: 0))
addConstraint(NSLayoutConstraint(item: label, attribute: .bottom, relatedBy: .equal, toItem: myTableView, attribute: .top, multiplier: 1.0, constant: 0))
addConstraint(NSLayoutConstraint(item: myTableView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: -15))
extension NextTable: UITableViewDelegate
extension NextTable: UITableViewDataSource
func numberOfSections(in tableView: UITableView) -> Int
return 1
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return 5
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
return UITableViewAutomaticDimension
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat
return 60
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! TableCell
cell.layoutIfNeeded()
return cell
第二个表格中的单元格
class TableCell: UITableViewCell
let label: UILabel =
let label = UILabel()
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.text = "Some text"
label.textColor = UIColor.black
label.sizeToFit()
label.backgroundColor = UIColor.red
return label
()
override init(style: UITableViewCellStyle, reuseIdentifier: String?)
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = UIColor.yellow
setupView()
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
func setupView()
addSubview(label)
addConstraintsWithFormat("V:|-8-[v0]-8-|", views: label)
addConstraintsWithFormat("H:|-5-[v0]-5-|", views: label)
这是我的代码的效果 没有第二个表,所以我创建了新类并在单元格中使用它作为新表视图
class InnerTableView: UITableView
override var intrinsicContentSize: CGSize
return self.contentSize
现在表格已显示,但尺寸太大 我该怎么做才能在单元格底部显示完整的第二个表格而没有空白空间。
【问题讨论】:
【参考方案1】:我在表格视图中为属性添加了覆盖:
override var intrinsicContentSize: CGSize
self.layoutIfNeeded()
return self.contentSize
我的表格的完整代码可以在我的GitHub上找到
【讨论】:
这是我在堆栈上找到的最好的帖子,以及 swift y tableView 标签!!!! 我有 1 个问题。如果我们这样做,表格会同时使用所有单元格还是只加载可见单元格并再次使用它们? 不错的方法。谢谢。 如果启用了动态类型,则内部tableview单元格不可见 这加上@MichaelScaria 的解决方案对我有用。【参考方案2】:在您的第一个表格视图中,在您将单元格出列后将其添加到您的 cellForRowAt
(可能需要稍微调整以适应您的实现):
cell.tableView.reloadData()
DispatchQueue.main.async
cell.tableView.scrollToRow(at: IndexPath(row: cell.numberOfRowsInInnerTableView.count.count - 1, section: 0), at: .bottom, animated: false)
DispatchQueue.main.async
cell.tableView.invalidateIntrinsicContentSize()
cell.tableView.layoutIfNeeded()
self.updateHeight()
然后在同一个类(外表视图)中定义一个函数updateHeight
:
func updateHeight()
UIView.setAnimationsEnabled(false)
tableView.beginUpdates()
tableView.endUpdates()
UIView.setAnimationsEnabled(true)
显然,这是一种 hacky,但本质上它允许单元格的 InnerTableView
知道所有内部单元格的实际高度并适当地调整外部 tableview 的大小。这种方法对我有用。
【讨论】:
您还需要使单元格的InnerTableView.estimatedRowHeight
小于其最短的单元格。
这个解决方案对我有用。你节省了我的时间。
这加上@Michal 的上述内容在离合器中为我带来了 - 伟大的工作【参考方案3】:
我尝试了上述解决方案,但对我没有用。我做了一点小改动,然后它对我来说效果很好。
A) 在我的 ViewController 中:
//注意:TestQuestionAnsOptionCell 是外部单元格
if let cell = tableView.dequeueReusableCell(withIdentifier: String(describing:TestQuestionAnsOptionCell.self), for: indexPath) as? TestQuestionAnsOptionCell
cell.reloadData()
setUpInnerCellListner(cell: cell)
cell.selectionStyle = .none
return cell
private func setUpInnerCellListner(cell:TestQuestionAnsOptionCell)
cell.reloadTable = // Closure called from TestQuestionAnsOptionCell
DispatchQueue.main.async
UIView.setAnimationsEnabled(false)
self.tbQuestion.beginUpdates()
self.tbQuestion.endUpdates()
UIView.setAnimationsEnabled(true)
此处使用的扩展名:
extension NSObject
class var className: String
return String(describing: self)
B) 在 TestQuestionAnsOptionCell 中:
class TestQuestionAnsOptionCell: UITableViewCell
var tblOptions: OwnTableView = OwnTableView()
var reloadTable:(()->(Void))?
override func awakeFromNib()
super.awakeFromNib()
setupView()
required init?(coder aDecoder: NSCoder)
super.init(coder: aDecoder)
private func setupView()
tblOptions.estimatedRowHeight = 60.0
tblOptions.rowHeight = UITableView.automaticDimension
tblOptions.delegate = self
tblOptions.dataSource = self
tblOptions.separatorStyle = .none
tblOptions.register(UINib(nibName: TestQuestionOptionCell.className, bundle: nil), forCellReuseIdentifier: TestQuestionOptionCell.className)
tblOptions.isScrollEnabled = false
addSubview(tblOptions)
addConstraintsWithFormat("H:|-30-[v0]-30-|", views: tblOptions)
addConstraint(NSLayoutConstraint(item: tblOptions, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 15))
addConstraint(NSLayoutConstraint(item: tblOptions, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: -15))
func reloadData()
tblOptions.reloadData()
使用 TableView 委托和数据源,我使用了以下内容:
extension TestQuestionAnsOptionCell:UITableViewDelegate, UITableViewDataSource
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
//NOTE: TestQuestionOptionCell is the inner cell
if let cell = tableView.dequeueReusableCell(withIdentifier: TestQuestionOptionCell.className, for: indexPath) as? TestQuestionOptionCell
cell.updateCell()
cell.selectionStyle = .none
return cell
return UITableViewCell()
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
return UITableView.automaticDimension
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
tblOptions.invalidateIntrinsicContentSize()
tblOptions.layoutIfNeeded()
reloadTable?()//Closure to update outer tableview
C) 内表视图用作:
class OwnTableView: UITableView
override var intrinsicContentSize: CGSize
self.layoutIfNeeded()
return self.contentSize
override var contentSize: CGSize
didSet
self.invalidateIntrinsicContentSize()
D) UIView 扩展为:
extension UIView
func addConstraintsWithFormat(_ format: String, views: UIView...)
var viewsDictionary = [String:UIView]()
for(index, view) in views.enumerated()
let key = "v\(index)"
view.translatesAutoresizingMaskIntoConstraints = false
viewsDictionary[key] = view
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: viewsDictionary ))
【讨论】:
【参考方案4】:您所需要的只是使 ParentTableView 的 cellForRowAt indexPath
中的内容大小无效parentCell.InsideTableview.invalidateIntrinsicContentSize()
【讨论】:
以上是关于具有动态高度的 UITableViewCell 内的 UITableView的主要内容,如果未能解决你的问题,请参考以下文章
动态 UITableViewCell 高度内的动态 UITextView?
Swift / 如何使用具有动态高度的 UITextView INSIDE UITableViewCell 与动态高度
具有动态高度的 UITableViewCell 内的 UITableView
具有动态内容、动态布局和动态高度的 UITableViewCell