How to know UITableViewCell high? - ios

I have UITabelViewController with custom MyUITableViewCell.
MyUITableViewCell class has #IBOutlet MyTextView:UITextView and method:
func getHeight() -> CGFloat
{
let height = (self.MyTextView?.contentSize)!.height
return height
}
I need set high for cell depend textView content size.
In UITabelViewController I set high for TableViewCell:
override func tableView(_tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
let cell = tableView(_tableView, cellForRowAtIndexPath: indexPath)
println(cell.getHeight())
return cell.getHeight()
}
But println show 20.0 for all cells on output, although I see that real cell high in table is different.

Simply check the UITableView's rowHeight method, like this:
tableView.rowHeight

implement heightForRowAtIndexPath:NSIndexPath

Related

How to narrow UITableView cell height if there is dynamic structure in Swift?

I have a tableView and cells. The Cells are loaded from a xib and they have a label with automatic height. I need to narrow one cell if the user taps on it.
I have tried hiding - doesn't work
I have tried removeFromSuperView()- doesn't work
Is there any alternative?
When setting up your tableViewCell store the height anchor you want to update
var yourLabelHeightAnchor: NSLayoutConstraint?
private func setupLayout() {
yourLabelHeightAnchor = yourLabel.heightAnchor.constraint(equalToConstant: 50)
// Deactivate your height anchor as you want first the content to determine the height
yourLabelHeightAnchor?.isActive = false
}
When the user clicks on a cell, notify the tableView that the cell is going to change, and activate the height anchor of your cell.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: "YourTableViewCellIdentifier") as? YourCell
self.tableView.beginUpdates()
cell?.yourLabelHeightAnchor?.isActive = true
self.tableView.endUpdates()
}
Did you try to do something like this:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var result: CGFloat
if (indexPath.row==0) {
result = 50 }
else {result = 130}
return result
}
This is just an example where height is changed for the first row. I tested on my application and it gave result like this.

Static UITableView, make a single cell with dynamic height in Swift

I have a static UITableView which has 12 rows and for 11 rows I know what the height needs to be.
I have a UILabel which has dynamic text which sits inside the 12 row, how do i go about making just that one cell to have a dynamic height based on the text in the UILabel.
I have tried the below code inside viewDidLoad, but it didn't work. The rows remained the same height. I have also set the lines = 0 for the UILabel
tableView.estimatedRowHeight = 100.0
tableView.rowHeight = UITableViewAutomaticDimension
Have you tried this?
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if (indexPath.row < 11) {
// Everything starts at 0, so this covers 0-10
// Whatever your cell height is
return <#fixedCellHeight#>
} else {
// This is your 12th cell (indexPath.row 11), as we start counting at 0
return UITableViewAutomaticDimension
}
}
Adding to #Adrian answer , if you are using static Cells , changing one cell to dynamic height , and others as they are you can edit it to this .
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == 11 {
// This is your 12th cell (indexPath.row 11), as we start counting at 0
return UITableViewAutomaticDimension
} else {
return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
}
}
Try this:
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
You can create 2 cell prototype cells for the table view. You give them 2 different id.
And then in your code you override this fonction
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = UITableViewCell()
if indexPath.row < 12 {
cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
} else {
cell = tableView.dequeueReusableCellWithIdentifier("cell12", forIndexPath: indexPath)
}
return cell
}
You Can create the dynamic cell with Text height then you can set the static and dynamic cell both in on table view
Here is link to dynamic cell with text How to change cell height dynamically in UITableView static cell
More elegant might be to just implement as suggested:
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Or in Objective C:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
But rather than the row specific logic, add constraints to your static cell's content in the Storyboard to keep the row heights constant. That way if you move rows or change content you don't need to change any code.

Dynamic table view cell, change of subview height programmatically

I have a UITableViewCell with a custom view cell.
In this view cell, I have a simple UIView called imgWrapper where I added constraints as follows:
width = 50
height = 50
leading to superview = 20
top to superview = 20
bottom to superview = 20
Those are the only constraints in there. And I left the hugging and compression to the default ones.
In my code I've set this:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 90
}
Then in in my rowAtIndex...I have this:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: LogCustomCell = tableView.dequeueReusableCellWithIdentifier("logCustomCell", forIndexPath: indexPath) as! LogCustomCell
var imgWrapperHeight: CGFloat = log.big ? 100 : 50
cell.imgWrapperHeight.frame.size.height = imgWrapperHeight
return cell
}
Once I compile and run it. All the cells are the same size.
Notes:
I checked if log.big was true/false and it does change.
I've also tried to do CGRect(x,y,width,height) but also didn't work.
I know I can do heightForRowAtIndexPath but I want to do animations and I know we can do something like this for labels (see printscreen) which makes the tableView know the height without defining it:
Any ideas what I'm doing wrong?
You need to put your height logic into the UITableViewDelegate method called tableView:heightForRowAtIndexPath, something like the following:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return (true ? 100 : 50) + 2 * 20
}
PS. I've written this in Swift 2, thus the overridekeyword.
Delegate method "heightForRowAtIndexPath" will do it for you. You can know cell index for which you are returning height from indexPath.row and hence return height accordingly.
i.e
if indexPath.row == 0
{
return 70
}
else if indexPath.row == 1
{
return 100
}
add
self.automaticallyAdjustsScrollViewInsets = false
before these two lines
tableView.estimatedRowHeight = 50
tableView.cellHeight = UITableViewCellAutomaticDimension
Solutions 1:
As mentioned by the users. You can set the row height in your UITableViewController like so:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return (true ? 100 : 50) + 2 * 20
}
Solution 2:
Set the height constraint on the element that will determine the height of cell. Then create an outlet in your VC for NSLayoutConstraint
While you are setting the content of the cell, you can do:
Ex:
#IBOutlet var imgWrapperHeightConstraint: NSLayoutConstraint!
...
override func tableView(tableView: UITableView, cellAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
imgWrapperHeightConstraint.constant = 50 // Or whatever value you want
}

How to dynamically change the height of a cell in a UITableView?

I have:
A UITableView in my UIViewcontroller
var searchResults: UITableView = UITableView();
A custom UITableViewCell class:
class CellSearchResult: UITableViewCell { ... }
I register my cell for the tableview:
searchResults.registerClass(CellSearchResult.self, forCellReuseIdentifier: "Cell");
I have my tableView method to fill my table:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CellSearchResult
cell.frame.height = 100; <<<< ERROR
...
}
I would like to change the height of my cell like:
cell.frame.height = 100; <<<< ERROR
How to do this ?
You need to override heightForRowAtIndexPath function. Example:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100
}
EDIT:
If you are not subclassing UITableViewController, you can't access tableView functions unless you conform UITableViewDelegate, UITableViewDataSource protocols. You can do this by
class myViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { ....
.
tableview.delegate = self
tableview.datasource = self
You have to implement the following to increase a particular row height at run time without calling the reloadData()
Example to increase height of row 10:
rowHightUpdateReqd = true
tableView.beginUpdates()
tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: 10, inSection: 0)], withRowAnimation: UITableViewRowAnimation.Fade)
tableView.endUpdates()
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
var rowHight: CGFloat = 50
if rowHightUpdateReqd == true{
rowHight = 100
rowHightUpdateReqd = false
}
return rowHight
}
If the heights of all cells are the same the most efficient way is to set the rowHeight property of the UITableView itself.
tableView.rowHeight = 100.0
There are performance implications to using
tableView:heightForRowAtIndexPath: instead of rowHeight. Every time a
table view is displayed, it calls tableView:heightForRowAtIndexPath:
on the delegate for each of its rows, which can result in a
significant performance problem with table views having a large number
of rows (approximately 1000 or more).
If the cells have variable heights you will need to implement the UITableViewDelegate method tableView:heightForRowAtIndexPath:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
var height = // do something to calculate height
return height
}
If you need to do expensive calculations in tableView:heightForRowAtIndexPath: it is also advisable to implement another UITableViewDelegate method, tableView:estimatedHeightForRowAtIndexPath: (available since iOS 7). Any code you put in this method needs to be efficient.
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100.0
}
Providing an estimate the height of rows can improve the user
experience when loading the table view. If the table contains variable
height rows, it might be expensive to calculate all their heights and
so lead to a longer load time. Using estimation allows you to defer
some of the cost of geometry calculation from load time to scrolling
time.
override func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
return 100.0;
}

Dynamic Height Issue for UITableView Cells (Swift)

Text data of variable length are being injected into tableview cell labels. In order for each cell height to be properly sized, I have implemented in viewDidLoad():
self.tableView.estimatedRowHeight = 88.0
self.tableView.rowHeight = UITableViewAutomaticDimension
This estimates the height to be 88.0 pixels and should resize the height automatically if larger. It works perfectly for cells that have yet to be scrolled to (as UITableViewAutomaticDimention is called upon scrolling to the cell), but not for the cells that are initially rendered onscreen upon loading the table with data.
I have tried reloading the data (as suggested in many other resources):
self.tableView.reloadData()
in both viewDidAppear() and viewWillAppear() and it did not help. I am lost.. does anyone know how to render the dynamic height for the cells loaded initially on screen?
Try This:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
EDIT
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Swift 4
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Swift 4.2
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
Define above Both Methods.
It solves the problem.
PS: Top and bottom constraints is required for this to work.
Here is example
Use this:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 300
and don't use: heightForRowAtIndexPath delegate function
Also, in the storyboard don't set the height of the label that contains a large amount of data. Give it top, bottom, leading, trailing constraints.
SWIFT 3
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 160
AND!!!
In storyBoard: You HAVE TO set TOP & BOTTOM constraints for your Label.
Nothing else.
This strange bug was solved through Interface Builder parameters as the other answers did not resolve the issue.
All I did was make the default label size larger than the content potentially could be and have it reflected in the estimatedRowHeight height too. Previously, I set the default row height in Interface Builder to 88px and reflected it like so in my controller viewDidLoad():
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 88.0
But that didn't work. So I realized that content wouldn't ever become larger than maybe 100px, so I set the default cell height to 108px (larger than the potential content) and reflected it like so in the controller viewDidLoad():
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 108.0
This actually allowed the code to shrink down the initial labels to the correct size. In other words, it never expanded out to a larger size, but could always shrink down... Also, no additional self.tableView.reloadData() was needed in viewWillAppear().
I know this does not cover highly variable content sizes, but this worked in my situation where the content had a maximum possible character count.
Not sure if this is a bug in Swift or Interface Builder but it works like a charm. Give it a try!
Set automatic dimension for row height & estimated row height and ensure following steps:
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Set automatic dimensions for row height
// Swift 4.2 onwards
table.rowHeight = UITableView.automaticDimension
table.estimatedRowHeight = UITableView.automaticDimension
// Swift 4.1 and below
table.rowHeight = UITableViewAutomaticDimension
table.estimatedRowHeight = UITableViewAutomaticDimension
}
// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// Swift 4.2 onwards
return UITableView.automaticDimension
// Swift 4.1 and below
return UITableViewAutomaticDimension
}
For Example: if you have a label in your UITableviewCell then,
Set number of lines = 0 (& line break mode = truncate tail)
Set all constraints (top, bottom, right left) with respect to its superview/ cell container.
Optional: Set minimum height for label, if you want minimum vertical area covered by label, even if there is no data.
Here is sample label with dynamic height constraints.
For Swift 3 you can use the following:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Dynamic sizing cell of UITableView required 2 things
Setting the the right constraint of your view inside the table view cell (mostly it includes giving your view proper top , bottom and traling constraints)
Calling these properties of TableView in viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 140
This is a wonderfull tutorial on self-sizing (dynamic table view cells) written in swift 3 .
In my case - In storyboard i had a two labels as in image below,
both labels was having desired width values been set before i made it equal. once you unselect, it will change to automatic, and as usual having below things should work like charm.
1.rowHeight = UITableView.automaticDimension, and
2.estimatedRowHeight = 100(In my case).
3.make sure label number of lines is zero.
In addition to what others have said,
SET YOUR LABEL'S CONSTRAINTS RELATIVE TO THE SUPERVIEW!
So instead of placing your label's constraints relative to other things around it, constrain it to the table view cell's content view.
Then, make sure your label's height is set to more than or equal 0, and the number of lines is set to 0.
Then in ViewDidLoad add:
tableView.estimatedRowHeight = 695
tableView.rowHeight = UITableViewAutomaticDimension
To make autoresizing of UITableViewCell to work make sure you are doing these changes :
In Storyboard your UITableView should only contain Dynamic Prototype Cells (It shouldn't use static
cells) otherwise autoresizing won't work.
In Storyboard your UITableViewCell's
UILabel has configured for all 4 constraints that is top, bottom,
leading and trailing constraints.
In Storyboard your UITableViewCell's
UILabel's number of lines should be 0
In your UIViewController's
viewDidLoad function set below UITableView Properties :
self.tableView.estimatedRowHeight = <minimum cell height>
self.tableView.rowHeight = UITableViewAutomaticDimension
For Swift i checked this answer in iOS 9.0 and iOS 11 also (Xcode 9.3)
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Here you need to add top, bottom, right and left constraints
For Swift 4.2
#IBOutlet weak var tableVw: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Set self as tableView delegate
tableVw.delegate = self
tableVw.rowHeight = UITableView.automaticDimension
tableVw.estimatedRowHeight = UITableView.automaticDimension
}
// UITableViewDelegate Method
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
Happy Coding :)
This is simple when doing 2 things:
setting the automatic height
tableView.rowHeight = UITableView.automaticDimension
creating all TableViewCells with FULL constraints from top to bottom. The last element MUST define some bottom spacing to end the cell.
So the layout engine can compute the cell heigth and apply the value correctly.
Unfortunately, I am not sure what I was missing. The above methods don't work for me to get the xib cell's height or let the layoutifneeded()or UITableView.automaticDimension to do the height calculation. I've been searching and trying for 3 to 4 nights but could not find an answer.
Some answers here or on another post did give me hints for the workaround though. It's a stupid method but it works. Just add all your cells into an Array. And then set the outlet of each of your height constraint in the xib storyboard. Finally, add them up in the heightForRowAt method. It's just straight forward if you are not familiar with the those APIs.
Swift 4.2
CustomCell.Swift
#IBOutlet weak var textViewOneHeight: NSLayoutConstraint!
#IBOutlet weak var textViewTwoHeight: NSLayoutConstraint!
#IBOutlet weak var textViewThreeHeight: NSLayoutConstraint!
#IBOutlet weak var textViewFourHeight: NSLayoutConstraint!
#IBOutlet weak var textViewFiveHeight: NSLayoutConstraint!
MyTableViewVC.Swift
.
.
var myCustomCells:[CustomCell] = []
.
.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Bundle.main.loadNibNamed("CustomCell", owner: self, options: nil)?.first as! CustomCell
.
.
myCustomCells.append(cell)
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let totalHeight = myCustomCells[indexPath.row].textViewOneHeight.constant + myCustomCells[indexPath.row].textViewTwoHeight.constant + myCustomCells[indexPath.row].textViewThreeHeight.constant + myCustomCells[indexPath.row].textViewFourHeight.constant + myCustomCells[indexPath.row].textViewFiveHeight.constant
return totalHeight + 40 //some magic number
}
I use these
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
Try
override func viewWillAppear(animated: Bool) {
self.tableView.layoutSubviews()
}
I had the same problem and it works for me.
You should just set all constraints for TOP, BOTTOM and HEIGHT for each object on cell view/views and remove exists middle Y position if have. Because where you didn't this, puts artifacts on another views.
For objective c this is one of my nice solution. it's worked for me.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
cell.textLabel.text = [_nameArray objectAtIndex:indexPath.row];
cell.textLabel.numberOfLines = 0;
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
We need to apply these 2 changes.
1)cell.textLabel.numberOfLines = 0;
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
2)return UITableViewAutomaticDimension;
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 88.0
And don't forget to add botton constraints for label
I was just inspired by your solution and tried another way.
Please try to add tableView.reloadData() to viewDidAppear().
This works for me.
I think the things behind scrolling is "the same" as reloadData. When you scroll the screen, it's like calling reloadData() when viewDidAppear .
If this works, plz reply this answer so I could be sure of this solution.
I had also got this issue initially, I had resolved my issue from this code
try avoiding the use of self.tableView.reloadData() instead of this code for dynamic height
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
When using a static UITableView, I set all the values in the UILabels and then call tableView.reloadData().
What worked for me was creating a height constraint on my custom cell that I set at runtime (I've got an expand/collapse button in each cell).
Then in heightForRowAt in the parent, I had to do a combination of suggested answers:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if let cell = tableView.cellForRow(at: indexPath) as? GroupTableViewCell {
return cell.heightConstraint.constant
}
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 88.0
}
I use the already calculated height constraint constant where it's available and UITableView.automaticDimension otherwise. This was the only way to get the correct height and maintain the correct cell state when the cell gets recycled.
I hear it's considered bad practice to reference the cell itself inside heightForRowAt, but I don't see another way of doing it with custom cell objects with dynamic heights whilst keeping all constraints satisfied.
self.Itemtableview.estimatedRowHeight = 0;
self.Itemtableview.estimatedSectionHeaderHeight = 0;
self.Itemtableview.estimatedSectionFooterHeight = 0;
[ self.Itemtableview reloadData];
self.Itemtableview.frame = CGRectMake( self.Itemtableview.frame.origin.x, self.Itemtableview.frame.origin.y, self.Itemtableview.frame.size.width,self.Itemtableview.contentSize.height + self.Itemtableview.contentInset.bottom + self.Itemtableview.contentInset.top);
Set proper constraint and update delegate methods as:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
This will resolve dynamic cell height issue. IF not you need to check constraints.
Swift 5 Enjoy
tablev.rowHeight = 100
tablev.estimatedRowHeight = UITableView.automaticDimension
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tablev.dequeueReusableCell(withIdentifier: "ConferenceRoomsCell") as! ConferenceRoomsCell
cell.lblRoomName.numberOfLines = 0
cell.lblRoomName.lineBreakMode = .byWordWrapping
cell.lblRoomName.text = arrNameOfRooms[indexPath.row]
cell.lblRoomName.sizeToFit()
return cell
}

Resources