Tableview inside a Tableview cell(using auto-resizing) — Neat and clean!
While dealing with a UITableview inside UITableViewCell without calculating the nested UITableViewCell content size is such a hectic thing I have ever seen in iOS App development.
I have tried many solutions to achieve this and finally, I have found a lead using a Stackview website’s solution and decided to create this article in brief.
This article will help you to achieve UITableview inside UITableViewCell with the Expandable/Collapsible feature.
To explain this, I have already created an XCode project which is having a UITableView inside another UITableViewCell without a nested UITableView’s height constraint:
This is my ViewController’s hierarchy:
In OuterTableCell, I take a HeaderView, which will hold the School name and add a UITapGestureRecognizer to detect click events on that.
OuterTableCell.swift
override func awakeFromNib() {
addTapEvent()
}
func addTapEvent() {
let panGesture = UITapGestureRecognizer(target: self, action: #selector(handleActon))
headerView.addGestureRecognizer(panGesture)
}
Meanwhile, on click of HeaderView, we will show/hide the InnerTableView.
OuterTableCell.swift
@objc private func handleActon() {
guard let isExpanded = schoolsData?.isExpanded else {
print("isExpanded variable not initialized")
return
}
innerTableView.isHidden = isExpanded
paddingView.isHidden = isExpanded
verticalLineView.isHidden = isExpanded
UIView.animate(withDuration: 0.3) {
self.stackView.setNeedsLayout()
self.helperDelegate?.heightChanged(index: self.index, value: !isExpanded)
}
schoolsData?.isExpanded = !isExpanded
}
Created a HelperDelegate in which I declare heightChanged() function that will call after UIStackView finish resizing the subviews. In addition, I update the required UI changes such as show/hide InnerTableView, VerticalLineView, etc.
OuterTableCell.swift
class OuterTableCell: UITableViewCell {
override func awakeFromNib() {
addTapEvent()
}
func addTapEvent() {
let panGesture = UITapGestureRecognizer(target: self, action: #selector(handleActon))
headerView.addGestureRecognizer(panGesture)
}
@objc private func handleActon() {
guard let isExpanded = schoolsData?.isExpanded else {
print("isExpanded variable not initialized")
return
}
innerTableView.isHidden = isExpanded
paddingView.isHidden = isExpanded
verticalLineView.isHidden = isExpanded
UIView.animate(withDuration: 0.3) {
self.stackView.setNeedsLayout()
self.helperDelegate?.heightChanged(index: self.index, value: !isExpanded)
}
schoolsData?.isExpanded = !isExpanded
}
}
extension OuterTableCell: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return schoolsData?.studentsData.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "InnerTableCell", for: indexPath) as? InnerTableCell else {
return UITableViewCell()
}
cell.studentsData = schoolsData?.studentsData[indexPath.row]
cell.verticalBottomLineView.isHidden = ((schoolsData?.studentsData.count ?? 0)-1) == indexPath.row
return cell
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
tableView.invalidateIntrinsicContentSize()
tableView.layoutIfNeeded()
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return schoolsData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "OuterTableCell", for: indexPath) as? OuterTableCell else {
return UITableViewCell()
}
cell.schoolsData = schoolsData[indexPath.row]
cell.helperDelegate = self
return cell
}
}
extension ViewController: HelperDelegate {
func heightChanged(index: Int, value: Bool) {
schoolsData[index].isExpanded = value
outerTableView.performBatchUpdates(nil)
}
}
Now you are good to go. You can even explore more by downloading the project.
You can download the source code from here.
I hope it helps! Also, feedback is welcome!