iOS: UITableView inside UITableViewCell with dynamic cell height - ios

I want to implement UITableView inside UITableViewCell with dynamic height of cell according to inside UITableView content size. How can I implement this any suggestions?
I want layout something like this...
Code work:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let identifier = "OrderHistoryTVCell" let cell: OrderHistoryTVCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? OrderHistoryTVCell
let tableInnerContentSize = cell.tableInner.contentSize.height
let nn = cell.viewAfterTable.frame.height+tableInnerContentSize
return nn
}

use following:
#IBOutlet weak var tableView: UITableView! //connect the table view
//do following in viewDidLoad method
tableView.estimatedRowHeight = 100 //Your estimated height
tableView.rowHeight = UITableViewAutomaticDimension
Also set these two properties of UILable from Attributes inspector section of storyboard:
lines to 0
Line break to word wrap
or you can also set these properties from code:
self.lblxyz.numberOfLines = 0
self.lblxyz.lineBreakMode = .byWordWrapping
Note - Add constraint properly in table view cell.

You have to do something like this:
In ui Master-Item # is be your section header. So take UILabel and add it as a section header.
Your sub-item is the prtotype-cell cell which contains one label.
Constraints for label inside cell.
In your cell first add UIView with constraints as follows:
Top , Bottom , Leading, Bottom to superView as 0.
Now add label and give constraints as follow.
Top , Bottom , Leading, Bottom to UIView as 8.
Give height constraint as per your requirement.
Give height relationship from = to >=.
Set label property line to 0 from storyboard.
Implement this lines in
//MARK:- TableView
public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat{
return 60 // return header height
}
public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
// return your section header i.e. master item label
}
public func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat{
return 50;
}
public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat{
return UITableViewAutomaticDimension
}
public func numberOfSections(in tableView: UITableView) -> Int{
// return number of section i.e. total count of master item.
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
// returns number of cells in each section
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
// return your custom cell here
//eg:
// cell.labelFood.text = your data
}
NOTE Don't forget to bind delegate and dataSource of tableView.

use this code in viewDidLoad()
self.tableView.estimatedRowHeight = 350
self.tableView.rowHeight = UITableViewAutomaticDimension
Use this Delegate
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return UITableViewAutomaticDimension
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
self.tableView.estimatedRowHeight = 160
return UITableViewAutomaticDimension
}
Also set these two properties of UILable.
#IBOutlet weak var label: UILabel!
label.numberOfLines = 0

Related

Stop TableView backgroundView from going past last cell in each section?

I have a UITableView in a UIViewController nested in a UITabBarController that has multiple sections, each with a custom section header that is a subclass of a UIView. The UItableView has all the proper delegates and data sources set up in code and the storyboard.
The footer is explicitly set to 0 in code.
For whatever reason, it seems that the background (in red below) bleeds past each of the UITableViewCells in each section.
My UITableView currently looks like this:
My settings for the tableview in the storyboard looks like this:
and finally, here is the code that controls the tableView, written as extensions on the UITableView subclass:
extension TestViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 64
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 34
}
}
extension TestViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = TestHeaderView()
view.setLabelWithValues(valueType: "Example", amount: 1)
return view
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as! TestTableViewCell
cell.testLabel?.text = "example"
cell.testLabel2?.text = "example"
cell.testLabel3?.text = "example"
return cell
}
}
How do I prevent the background from going past each section of cells?
Figured it out:
Looks like I had to set the footer size to 0 (which then defaults to 1) in the storyboard size inspector column like so:
This will leave a single pixel width of footer, so switch the background color to whatever your cell is.

UILabel taking space when number of lines is fixed in UITableViewCell?

I have a UILabel in UITableViewCell.I have given it's constraints as Top,Bottom,Leading ,Trailing. If I give it numberoflines as 0 then it does not take any space.But if give it fix number of lines then it takes extra space from top & bottom. Please tell me what is the issue ?
Code for UITableView Delegates
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Constraints Given as
Screenshot for the UITableViewCell
When you give number of lines 0 along with the above constraints, the UILabel will have auto height, which means the AutomaticDimension for tableview cell will height relative to UILabel. When UILabel's text is empty, it's height becomes zero.
To overcome this situation, you can specify either of two things:
Minimum height for the UILabel (e.g. in the constraint give height > 30)
Estimated height for UITableViewCell using delegate method
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let lbl = cell.contentView.viewWithTag(10) as! UILabel
lbl.text = "abcljfdslkfj;dlskfjd;slkfjds;ljdsfldjsf;ldsjfdslkfjds;lfjds;lfjdfl;jdsf;lkdjsf;ldsjf;ldskjd;slfjdsl;fjd;fljdfl;kjsdflljksdabcljfdslkfj;dlskfjd;slkfjds;ljdsfldjsf;ldsjfdslkfjds;lfjds;lfjdfl;jdsf;lkdjsf;ldsjf;ldskjd;slfjdsl;fjd;fljdfl;kjsdflljksdabcljfdslkfj;dlskfjd;slkfjds;ljdsfldjsf;ldsjfdslkfjds;lfjds;lfjdfl;jdsf;lkdjsf;ldsjf;ldskjd;slfjdsl;fjd;fljdfl;kjsdflljksdabcljfdslkfj;dlskfjd;slkfjds;ljdsfldjsf;ldsjfdslkfjds;lfjds;lfjdfl;jdsf;lkdjsf;ldsjf;ldskjd;slfjdsl;fjd;fljdfl;kjsdflljksdabcljfdslkfj;dlskfjd;slkfjds;ljdsfldjsf;ldsjfdslkfjds;lfjds;lfjdfl;jdsf;lkdjsf;ldsjf;ldskjd;slfjdsl;fjd;fljdfl;kjsdflljksdabcljfdslkfj;dlskfjd;slkfjds;ljdsfldjsf;ldsjfdslkfjds;lfjds;lfjdfl;jdsf;lkdjsf;ldsjf;ldskjd;slfjdsl;fjd;fljdfl;kjsdflljksdabcljfdslkfj;dlskfjd;slkfjds;ljdsfldjsf;ldsjfdslkfjds;lfjds;lfjdfl;jdsf;lkdjsf;ldsjf;ldskjd;slfjdsl;fjd;fljdfl;kjsdflljksdabcljfdslkfj;dlskfjd;slkfjds;ljdsfldjsf;ldsjfdslkfjds;lfjds;lfjdfl;jdsf;lkdjsf;ldsjf;ldskjd;slfjdsl;fjd;fljdfl;kjsdflljksdabcljfdslkfj;dlskfjd;slkfjds;ljdsfldjsf;ldsjfdslkfjds;lfjds;lfjdfl;jdsf;lkdjsf;ldsjf;ldskjd;slfjdsl;fjd;fljdfl;kjsdflljksdabcljfdslkfj;dlskfjd;slkfjds;ljdsfldjsf;ldsjfdslkfjds;lfjds;lfjdfl;jdsf;lkdjsf;ldsjf;ldskjd;slfjdsl;fjd;fljdfl;kjsdflljksdabcljfdslkfj;dlskfjd;slkfjds;ljdsfldjsf;ldsjfdslkfjds;lfjds;lfjdfl;jdsf;lkdjsf;ldsjf;ldskjd;slfjdsl;fjd;fljdfl;kjsdflljksdabcljfdslkfj;dlskfjd;slkfjds;ljdsfldjsf;ldsjfdslkfjds;lfjds;lfjdfl;jdsf;lkdjsf;ldsjf;ldskjd;slfjdsl;fjd;fljdfl;kjsdflljksdabcljfdslkfj;dlskfjd;slkfjds;ljdsfldjsf;ldsjfdslkfjds;lfjds;lfjdfl;jdsf;lkdjsf;ldsjf;ldskjd;slfjdsl;fjd;fljdfl;kjsdflljksdabcljfdslkfj;dlskfjd;slkfjds;ljdsfldjsf;ldsjfdslkfjds;lfjds;lfjdfl;jdsf;lkdjsf;ldsjf;ldskjd;slfjdsl;fjd;fljdfl;kjsdflljksdabcljfdslkfj;dlskfjd;slkfjds;ljdsfldjsf;ldsjfdslkfjds;lfjds;lfjdfl;jdsf;lkdjsf;ldsjf;ldskjd;slfjdsl;fjd;fljdfl;kjsdflljksd"
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
}

iOS/Swift: Dynamic UITableViewCell row height with embedded UITableView not working

I have a UITableViewCell which contains a UICollectionView on top and a UITableView on the bottom. The idea is that a dynamic amount of cells will be created in the inner UITableView and the parent UITableViewCell that encloses the two subviews will increase its height proportionally.
I am trying to take advantage of the estimatedRowHeight + UITableViewAutomaticDimentionfeature of the UITableViewCell that will allow the cell height to increase dynamically. However, it is not working. It completely removes the embedded UITableView from view.
I have not made any constraints that limit the height of the enclosed UITableView, so I am not sure why it is not working.
Here is the implementation that attempts to make a dynamically sized UITableViewCell:
class OverviewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Enclosed Table View Example"
}
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 325 // Height for inner table view with 1 cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 45
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "appHeaderCell") as! AppHeaderCell
return cell
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "appCell", for: indexPath) as! AppCell
return cell
}
}
My only guess is that the constraint bottom = Inner Table View.bottom + 7 is causing the issue, but the entire view falls apart when this constraint is removed.
What can I do to make the complex outer UITableViewCell dynamically adjust height based on the number of cells in the embedded UITableView?
Although it may seem like a good idea, the use of UITableViewAutomaticDimension in conjunction with estimatedRowHeight is not good to use in scenarios like this where we have general content inside table view cells. Making use of the heightForRowAt method, you can calculate the size of each individual cell before it centers the table.
Once we know how many cells will be in the inner table, you need to create an array whose elements correspond to the number inner cells that will ultimately determine the height of the outer cell, as all other content is constant.
let cellListFromData: [CGFloat] = [3, 1, 4]
This array will give us the number of sections in our outer table view:
func numberOfSections(in tableView: UITableView) -> Int {
return cellListFromData.count
}
We will convert each element in this array to a cell height in the following way:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let prototypeCell = tableView.dequeueReusableCell(withIdentifier: "appCell") as! AppCell
let baseHeight = betweenCellSpacing + prototypeCell.innerCollectionView.contentSize.height + prototypeCell.innerTableView.sectionHeaderHeight + outerTableView.sectionHeaderHeight
let dynamicHeight = prototypeCell.innerTableView.contentSize.height - prototypeCell.innerTableView.sectionHeaderHeight
return baseHeight + (dynamicHeight * cellListFromData[indexPath.section])
}
That is, inside of the heightForRowAt method, we dequeue a prototype cell that will not be used in the resulting view (as dequeueReusableCell is not called inside cellForRowAt in this case). We use this prototype cell to extract information about what is constant and what is dynamic about the cell's content. The baseHeight is the accumulated height of all the constant elements of the cell (plus the between-cell spacing) and the dynamicHeight is the height of an inner UITableViewCell. The height of each cell then becomes baseHeight + dynamicHeight * cellListFromData[indexPath.section].
Next, we add a numberOfCells variable to the class for the custom cell and set this in the cellForRowAt method in the main table view:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "appCell", for: indexPath) as! AppCell
cell.numberOfCells = Int(cellListFromData[indexPath.section])
cell.innerTableView.reloadData()
return cell
}
numberOfCells is set with the same cellListFromData that we used to get the height of the cell. Also, it is critical to call reloadData() on the inner table view after setting its number of cells so that we see that update in the UI.
Here is the full code:
class OverviewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var outerTableView: UITableView!
let cellSpacing: CGFloat = 25
let data: [CGFloat] = [3, 1, 4]
override func viewDidLoad() {
super.viewDidLoad()
}
func numberOfSections(in tableView: UITableView) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let prototypeCell = tableView.dequeueReusableCell(withIdentifier: "appCell") as! AppCell
let baseHeight = cellSpacing + prototypeCell.innerCollectionView.contentSize.height + prototypeCell.innerTableView.sectionHeaderHeight + outerTableView.sectionHeaderHeight
let dynamicHeight = prototypeCell.innerTableView.contentSize.height - prototypeCell.innerTableView.sectionHeaderHeight
return baseHeight + (dynamicHeight * data[indexPath.section])
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "appCell", for: indexPath) as! AppCell
cell.numberOfCells = Int(data[indexPath.section])
cell.innerTableView.reloadData()
return cell
}
}
class AppCell: UITableViewCell, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var innerCollectionView: UICollectionView!
#IBOutlet weak var innerTableView: UITableView!
var numberOfCells: Int = 0
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfCells
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "featureHeaderCell") as! BuildHeaderCell
return cell
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "innerCell", for: indexPath) as! InnerCell
return cell
}
}
Methods relating to configuring the inner collection view is not included here as it is not related to the problem.

Dynamically adjust the height of the tableview cell based on content - iOS Swift

I am trying to set the row height dynamically based on the content set in the detail text label, by using the below code in Part A.
I am inserting few lines of text into a cell's detail text label as shown below in Part B
I've looked at other similar questions but none have helped.
Can some one please advice how I can adjust the row height dynamically based on the content of the detail text label.
Part A
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Also tried
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Part B
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "daysIdentifier", for: indexPath)
cell.textLabel?.text = days[indexPath.row]
cell.detailTextLabel?.numberOfLines = 0
var joinedString = self.availabilityTimeDict[dayName]?.joined(separator: " \n ")
cell.detailTextLabel?.text = joinedString
return cell
}
Use custom cell and labels.
Set up the constrains for the UILabel. (top, left, bottom, right)
Set lines of the UILabel to 0
Add the following code in the viewDidLoad method of the ViewController:
tableView.estimatedRowHeight = 68.0
tableView.rowHeight = UITableView.automaticDimension
// Delegate & data source
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableView.automaticDimension;
}
Swift 4:
// Delegate & data source
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Swift 4.2:
// Delegate & data source
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
give top,bottom,leading and trailing to your lable inside content of tableview.Use below methods of table view
func tableView(_ tableView: UITableView,heightForRowAt indexPath:IndexPath) -> CGFloat
{
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat
{
return 100
}
First setup a custom cell and add a label and set its number of lines to zero and give bottom, top, leading, trailing constraints to cell's content view(dont give the height) also give a custom height to cell in size inspector then in viewDidLoad you just need to do,
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100.0

How to create static cells with dynamic cell heights with Swift

I have looked at several tutorials that show how to set dynamic cell heights, but all of them only show it if you are using dynamic cells by setting the appropriate constraints and using UITableViewAutomaticDimension. However, I would like to do this for static cells.
I have a table view controller in my app that contains several cells that display a category with a disclosure indicator in which there is a title and then a description and the size of the description text varies. Thus, I would like the height of the cell to be dynamic per the size of the description text.
The reason why I am using static cells and not dynamic cells is because above the category cells I have a few cells with some designs that I could only get by using static cells and storyboarding.
I am using iOS9, Xcode 7.3 and Swift 2.2
UPDATE
Table View Controller Code
import UIKit
class CategoriesTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 140
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 2
}
}
UPDATE 2
Per accepted answer below I added the two methods below to my table view controller and removed the 2 lines I had in viewDidLoad(). This did it for me! Keep in mind that I had to make sure all the labels in each cell used a top, bottom, leading and trailing constraint. Also, the number of lines for each label must be set to 0.
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
UILabel:
Set numberOfLines = 0 via programmatically or set lines to zero in attributes inspector of the UILabel.
UITextView:
Let's select your text view and uncheck the scrolling enabled like as below image.
Add the below code to your view controller:
Swift 5:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
Swift 2.2:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
For Swift 3
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
All you have to do is set proper AutoLayout inside the cell, so the height increases based on the length of the description text.
Then you need to set the following in your viewDidLoad
tableview.rowHeight = UITableViewAutomaticDimension
tableview.estimatedRowHeight = 44
Please note: you need to set the numberOfLines of UILabel to 0
#Forge, add please to your topic
Swift 4 variant:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
#Forge answer Swift 5 variant,
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}

Resources