Tableviewcell height with inside tableView - ios

I have a tableView in which every cell contains another tableView with dynamic row height.
My question is how can I set the row height of the first table to fit the height of the inner tableView?
I'm using this code but it does not work as it should.
var heights: [CGFloat] = []
var loades: [Bool] = []
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return self.heights[indexPath.row]
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "eventCell", for: indexPath) as! EventCell
cell.date.text = ev.date
cell.delegate = self
cell.event = ev
self.heights[indexPath.row] = cell.tv.frame.height
if self.loades[indexPath.row] == false{
self.loades[indexPath.row] = true
tableView.reloadRows(at: [indexPath], with: .none)
}
return cell}

I'd try getting the nested tableview's contentSize in your heightForRow method.

I hope you can access to your tableview.contentSize.height, the contentSize gets the value of all tableview content if your tableview is dynamic, the content of this height you can divide in the number of rows what you have. This is just a simply solution, some like this:
let size = tableView.contentSize.height
let cellSize = size / models.count
where models is the array of objects printed in tableviewcell inside tableview
Recommend:
Put a height constraint inside your cell to your tableView, and try to refresh with de sizeCell sum
cell.tableViewInsideCell.reloadData()
cell.heightConstraint.constant = tableView.contentSize.height
and set
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}

Related

Cannot Set Dynamic TableView Row Height

I'm trying to set dynamic row height in TableView. But unfortunately is not working.
The only thing is working is to set the heightForRowAt indexPath to a constant number.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 470 //or any other number
}
If I delete this function, or try to set it to automatic the cell will not show at all. Something like this:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
I also tried to use:
self.tableView.estimatedRowHeight = UITableView.automaticDimension //Tried even with a number
self.tableView.rowHeight = UITableView.automaticDimension
But seems nothing to work besides using a constant height in heightForRowAt indexPath function. Which is not what I want, I want cell to be expanded based on content inside the cell.
I also tried to set height in heightForRowAt indexPath function to automatic but set a fixed height on constraints in the content of the cell xib file, still same (I did just to see what happens). So seems, something is wrong around this function.
Note:
My TableView and UITableViewCell are divided in a storyboard and a xib files.
If you want to set the dynamic tableview row height, you must use autolayout.
Please set the constraint on the UI components in the cell as shown in the image below.
I used UILabel as sample code. For UILabel, be sure to set Lines to 0.
Here is the sample code
class ViewController: UIViewController {
let stringArr = ["aaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"]
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableView.automaticDimension
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return stringArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as? TestCell else {
return UITableViewCell()
}
cell.label.text = stringArr[indexPath.row]
return cell
}
}
You need to register the cell in the tableview.
tableView.register(UINib(nibName: “YourCellNibName”, bundle: nil), forCellReuseIdentifier: “YourCellReuseIdentifier”)

Get the row height in the HeightForRowAt

I have a UITableViewController with a custom UITableViewCell. Each cell has 2 labels. When the cell is selected it expands to a fixed value, I do it via tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat . I also have set the rowHeight = UITableViewAutomaticDimension , as some cells have to display multiple lines of text.
What I want to achieve is when a cell needs to be expanded I want to add 50 points to its current height. So here's the question, how can I get current height of the cell, when the rowHeight = UITableViewAutomaticDimension is set?
Here's my code for the fixed height for the selected state:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if selectedIndexPath == indexPath {
return selectedHeight
}else{
return tableView.estimatedRowHeight
}
}
EDIT: I also need to change it after that, by adding some variable to it.
Building on HamzaLH's answer you might do something like this...
import UIKit
class TableViewController: UITableViewController {
var selectedRow: Int = 999 {
didSet {
tableView.beginUpdates()
tableView.endUpdates()
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == selectedRow { //assign the selected row when touched
let thisCell = tableView.cellForRow(at: indexPath)
if let thisHeight = thisCell?.bounds.height {
return thisHeight + 50
}
}
return 60 //return a default value in case the cell height is not available
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedRow = indexPath.row
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
cell.detailTextLabel?.text = "test"
return cell
}
}
I'm animating the expansion of the height with a didSet when a the selectedRow is altered.
Also, don't forget you may still need to connect your dataSource and delegate by dragging the outlets in Interface Builder to your View Controller in the storyboard. After that you still need to add this to ViewDidLoad in your ViewController's swift file.
tableView.delegate = self
tableView.dataSource = self
I also have an Outlet declared for the tableView like below, and connected in Interface Builder storyboard.
#IBOutlet weak var tableView: UITableView!
You need to get the cell using cellForRow and then get the height of the cell.
let cell = tableView.cellForRow(at: indexPath)
let height = cell.bounds.height

Self sizing cells for nested table and collection Views

I'm trying to build a table view, one of the cells in this TableView is a tabs widget.
so I'm creating this tab widget as a CollectionView. the cells of the collectionView have different subViews and each cell has it's own size.
what I'm trying to achieve is Self-sizing cells in the collectionView, but it is not working.
I'll provide you with the way I used so I can get your help.
//MARK: TableView
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DetailsTabs", for: indexPath) as! DetailsTabs /*this cell has 2 collectionViews. The first collectionView is the tabs and the second collectionView is the content that should be diplayed. I gave the first collectionView a fixed height, trailing leading and top. the second collectionView has trailing, leading bottom, and top to the first collectionView*/
cell.postDetail = postDetail
cell.widgets = self.Template
cell.parentController = self
cell.Shownavigationbar = self.Shownavigationbar
return cell
}
//MARK: DetailsTabs Class
override func awakeFromNib() {
//code to setUp the collectionViews
//pagesLayout is the UICollectionViewFlowLayout for the second collectionView
pagesLayout.estimatedItemSize = CGSize(width: UIScreen.main.bounds.width, height: 1)
}
//MARK: IN collectionView Cells I added this function:
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
setNeedsLayout()
layoutIfNeeded()
let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
var frame = layoutAttributes.frame
frame.size.height = ceil(size.height)
layoutAttributes.frame = frame
return layoutAttributes
}
note: the content of the collectionView cells might be webView or tableview.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
you should use UITableViewAutomaticDimension in estimatedHeightForRowAtIndexPath
hope this is working fine for you...!

Default UITableViewCell height is to big for the text

I have an UITableView with a default cell where I just add a string:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = tasks[indexPath.row].name
cell.textLabel?.textColor = .white
cell.textLabel?.textAlignment = .center
return cell
}
My problem is that the cell is too big for just some normal text. Here is a screenshot, the cell has the blue background:
I didn't have this issue before, when my data source was an array on String's. The issue appeared after I changed from Strings to custom objects. Not sure if this is related.
How can I fix this ? I tried setting the row height to custom value, but it doesn't have any effect.
edit with table view code:
extension TasksViewController {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = tasks[indexPath.row].name
cell.textLabel?.textColor = .white
cell.textLabel?.textAlignment = .center
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dummyTxt.textField.text = tasks[indexPath.row].name
dummyTxt.becomeFirstResponder()
dummyTxt.textField.becomeFirstResponder()
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let delete = UITableViewRowAction(style: .destructive, title: "Delete") { (_, index) in
self.tasks.remove(at: index.row)
}
return [delete]
}
}
Set this on tableView:
tableView.estimatedRowHeight = 44
tableView.rowHeight = UITableViewAutomaticDimension
Do NOT override heightForRowAt delegate method.
And then just make sure that the prototype cell that you registered as "Cell" has proper constraints that can be used to calculate its height. In your case make sure that both top and bottom anchors of the label are constrained to the top and bottom anchors of the cell.
Use following delegate method with returning some integer value, if you need same size for every cell.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 40
}
Hopes It may help you.
Try this:
To set automatic dimension for row height & estimated row height, ensure following steps to make, auto dimension effective for cell/row height layout.
Assign and implement tableview dataSource and delegate
Assign UITableViewAutomaticDimension to rowHeight & estimatedRowHeight
Implement delegate/dataSource methods (i.e. heightForRowAt and return a value UITableViewAutomaticDimension to it)
-
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Don't forget to set dataSource and delegate for table
table.dataSource = self
table.delegate = self
// Set automatic dimensions for row height
table.rowHeight = UITableViewAutomaticDimension
table.estimatedRowHeight = UITableViewAutomaticDimension
}
// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
For label instance in UITableviewCell
Set number of lines = 0 (& line break mode = truncate tail)

Self sizing cell not working until scrolling some items

I'm using self sizing UITableViewCell with some labels inside and all labels lines number is 0.
tableView.estimatedRowHeight = 284
tableView.rowHeight = UITableViewAutomaticDimension
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SearchCell", for: indexPath) as! NodeCell
let nodemodel = nodes[indexPath.row]
cell.like.text = Utils.convert_string(string: String(nodemodel.like))
cell.address.text = nodemodel.address
cell.avatar.downloadedFrom(link: "www.example.com")
cell.avatar.layer.cornerRadius = cell.avatar.frame.size.width / 2
cell.avatar.layer.borderWidth = CGFloat(integerLiteral: 1)
cell.avatar.layer.borderColor = UIColor.black.cgColor
cell.avatar.clipsToBounds = true
cell.title.text = nodemodel.node_name
cell.layoutIfNeeded()
return cell
}
after running application all of my labels limited to single line and texts not showing completely until scrolling some items and then scrolling back. what is wrong? I did layoutIfNeeded() before returning cell but it's not working
problem is solved after changing UIStackView distribution mode from Fill Proportionally to Equal Spacing
Try using UITableView delegate methods instead:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return UITableViewAutomaticDimension
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat
{
return 284
}

Resources