How can I set UITableView cell height programmatically in Swift? - ios

How to set view height programmatically? I have this code:
cell.viewMain.frame = CGRectMake(cell.viewMain.frame.origin.x, cell.viewMain.frame.origin.y, cell.viewMain.frame.size.width, 65.0)
But that is not working.
UPDATE:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("updateCell", forIndexPath: indexPath) as! UpdateCell
if self.data[indexPath.row] == "b" {
tbUpdate.rowHeight = 85
cell.viewMain.frame = CGRectMake(cell.viewMain.frame.origin.x, cell.viewMain.frame.origin.y, cell.viewMain.frame.size.width, 65.0)
}else{
tbUpdate.rowHeight = 220
cell.viewMain.frame = CGRectMake(cell.viewMain.frame.origin.x, cell.viewMain.frame.origin.y, cell.viewMain.frame.size.width, 200.0)
}
return cell
}

TableView cells have their height set by the table view.
You need to implement UITableViewDelegate tableView:heightForRowAtIndexPath:.

first, rowHeight changes the height of all rows in your table. if you want specific height for specific rows, implement tableView:heightForRowAtIndexPath: method. remove tbUpdate.rowHeight in your code first.

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var cellHeight:CGFloat = CGFloat()
if indexPath.row % 2 == 0 {
cellHeight = 20
}
else if indexPath.row % 2 != 0 {
cellHeight = 50
}
return cellHeight
}

You can try using autolayout to create a height constrait and then connect the constraint to an outlet. Then set the constant for that constraint,
var y: Float
if x==0{ //some condition
y = 10
}else if x==1{ //some condition
y = 20
}
cell.viewMainHeightConstraint.constant = y
cell.view.layoutIfNeeded()
Put this in your cellForRowAtIndexPath
Edit : I misinterpreted the question as asking for another view within the cellView, if so the delegate method heightForRowAtIndexPath is indeed correct as stated in the above answer.
An example:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
if x == 0{
return 100.0 //Choose your custom row height
} else {
return 50
}
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var height:CGFloat = CGFloat()
if indexPath.row == 0 {
height = 80
}
else if indexPath.row == 1 {
height = self.view.frame.size.height - 44 - 64 // 44 is a tab bar height and 64 is navigationbar height.
print(height)
}
return height
}

Related

Static tableview How do I make the last visible row to occupy remaining space in

There are 5 static cells. When I show all 5 cells, it's fine but If I hide some cell by returning height as zero and the last visible cell is not same as last cell of tableview in storyboard then confusion comes as to how to do that ?
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return hideRow(at: indexPath) ? 0: UITableView.automaticDimension
}
IMO , If last visible row in tableview and lastrow in tableview is same
return indexPath.row == 4 ? 150 : tblHeight - 150
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if hideRow(at: indexPath) {
return 0
}
else {
let tblHeight = infoTable.bounds.size.height
if indexPath.row == 4 {
return tblHeight - yPlusHeightOfPreviousCell
}
return UITableView.automaticDimension
}
}
But the problem now what if 3rd is last row and for 4th and 5th I'm returning 0 as height to hide it.
Then how to make the last visible cell expand and occupy the full space
Add an empty UIView as the table footer view:
class StaticTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
}
}
Edit After re-reading your question...
The above code will "hide" the empty rows.
If your goal is to make the "last visible row" fill the remaining height, that will be more considerably complex.
Since your current approach is using a static table view and you won't be doing any scrolling, a much easier approach would be to use a UIStackView with custom views instead of cells. Then everything would be handled by auto-layout, constraints, and the stack view, and you wouldn't need to do any calculating.
Edit 2a - move code to a function
Add these vars to your table view controller class:
var numRows: Int = 5
var lastVisibleRow: Int = -1
var lastRowHeight: CGFloat = -1
var curTableWidth: CGFloat = 0
Add this function:
func updateTable() -> Void {
lastVisibleRow = -1
var h: CGFloat = 0
// find last visible row
// and sum of row heights
for i in 0..<numRows {
let r = tableView.rectForRow(at: IndexPath(row: i, section: 0))
h += r.height
if r.height > 1.0 {
lastVisibleRow = i
}
}
// get rect of last visible row
let r = tableView.rectForRow(at: IndexPath(row: lastVisibleRow, section: 0))
// subtract its height from the sum of heights
h -= r.height
// set calculated last row height
lastRowHeight = tableView.frame.height - h + tableView.bounds.origin.y
// reload the last visible row
//tableView.reloadRows(at: [IndexPath(row: lastVisibleRow, section: 0)], with: .none)
tableView.reloadData()
}
Call it from viewDidLayoutSubviews() anytime the table width changes:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// we only run the following block
// if the tableView width has changed
if curTableWidth != tableView.frame.width {
curTableWidth = tableView.frame.width
updateTable()
}
}
and change your heightForRowAt to this:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if hideRow(at: indexPath) {
// row should be hidden
return 0
} else if indexPath.row == lastVisibleRow {
// if it's the last visible row,
return lastRowHeight
}
// else
return UITableView.automaticDimension
}
Then, anytime you change the rows (based on user-interaction or whatever), call reloadData on the tableView, and then call updateTable() to re-calculate the last visible row and its height.

How to return in UITableView fixed row height or dynamic row height according to a certain condition?

I have a tableView where I need to return a fixed row height in some cases, but a dynamic height that takes the size of the content of a label.
How could I achieve both cases for same tableView.
Because when I implement
func tableView(_ tableView: UITableView, heightForRowAt indexPath:IndexPath) -> CGFloat {
return 75
}
The height is always fixed.
In order to do that first of all you need to set the label’s number of lines to zero.
Then give the label constraints to the tableView cell from for edges. (I assume you already did and this is not the main problem).
Then in your viewDidLoad add:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 300
and implement heightForRow function as follows:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if condition == true {
return 200
//This will return fixed height
}
return UITableViewAutomaticDimension
}
Use your delegate height method like this
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row = 1 || indexPath.row = 4 || indexPath.row = 6{// your required cells index
return 75
}
else{
return UITableViewAutomaticDimension
}
}
Set the number of lines of your label to 0.
Also implement heightForRow and estimatedHeightForRowAt:
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if yourRequiredCondition {
return requiredHeight
}
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if yourRequiredCondition {
return requiredHeight
}
return UITableViewAutomaticDimension
}
As you're on the right track. Override the heightForRowAtIndexPath of UITableView's datasource method, Something like below.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *labelString = #"Your cell text, probably you should have an array accessed through indexPath";
CGSize defaultSize = CGSizeMake(yourLabel.width, CGFLOAT_MAX);
//Get the height of the label, based on the text.
CGFloat textHeight =
[labelString boundingRectWithSize:defaultSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:[yourfont]}
context:nil].size.height;
// Now the textHeight will have the height of your label,
// based on your text for each cell.
// While returning you can return the textHeight + constant height.
// Constant height, is nothing but other static elements of cell's
// Aggregated height & spaces.
return textHeight+CONSTANT_HEIGHT;
}

How to program static cells to take the height from storyboard while programming other cells to take height from code

I have a uitableview controller with STATIC cells in two sections. I have adjusted the height of cells 0 & 1 in section 0 using storyboard. However, I have used code to expand a date picker after a UIswitch is toggled and a cell is tapped in section 1.
My problem is that the code is overriding cells 0 and 1 in section 0. How do I force those cells to take their height from storyboard? If that is not possible, how would I code their height so that the cell expansion code does not override it?
Notes: I am new and don't fully understand the code that I used to make the expansion happen, so if I need to change that, let me know. Code pasted below.
var pickerVisible = false
// MARK: - COLLAPSABLE CELL
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 1 && indexPath.row == 2 {
pickerVisible = !pickerVisible
tableView.reloadData()
}
tableView.deselectRow(at: indexPath, animated: true)
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 1 && indexPath.row == 2 && toggle.isOn == false {
return 0.0
}
if indexPath.section == 1 && indexPath.row == 3 {
if toggle.isOn == false || pickerVisible == false {
return 0.0
}
return 165.0
}
return 44.0
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 44.0
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 {
// set height for rows in section 0
} else if indexPath.section == 1 {
// set height for rows in section 1
} else {
// default height for your tableview cell
return 44.0
}
}
How do I force those cells to take their height from storyboard?
-> I think you don't really need this.
How would I code their height so that the cell expansion code does not override it?
-> Its pretty easy to do what you want (sample code above)
I have found another answer that really gets to the root of what my problem was -- how to program one cell to be an exact height and have all the other cells take the height from what is put together in storyboard.
The answer is instead of returning a number in heightForRowAt, you can do the following:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0 {
return 170.0
}
// OTHERWISE, RETURN WHATEVER THE TABLEVIEW WANTS.
return self.tableView.rowHeight

Dynamically change TableView Cell height

Swift/Objective C - Simple Way to Dynamically change TableView Cell height
In my table view cell break first line and third line, every label will dynamic
Add Constraints to Image View (top, Leading, trailing, Height)
Don't Add Bottom Constraints
Add Constraints to Each label (top, Leading, trailing, Bottom)
Add Constraints to last label (top, Leading, trailing, Bottom)
Set each label
Number of Line = 0
Line Breaks mode = word warp
Table View Datasource and Delegate Methods
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("PropertyListCell", forIndexPath: indexPath) as UITableViewCell!
imgProperty = viewBg.viewWithTag(111) as! RemoteImageView
lblPropertyName = viewBg.viewWithTag(112) as! UILabel
lblPrice = viewBg.viewWithTag(113) as! UILabel
lblAddress = viewBg.viewWithTag(114) as! UILabel
lblAreaPerSquare = viewBg.viewWithTag(115) as! UILabel
imgProperty.imageURL = NSURL(string: "Image Url")
lblPropertyName.text="Jai Maharashtra Apartment"
lblPrice.text="Rs. 900 - 10000"
lblAddress.text="411041,Maharashtra Sadan, Pune, Maharashtra , India" // just address changed.
lblAreaPerSquare.text="500 Square Meter"
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
return UITableViewAutomaticDimension
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
return 44.0
}
Now All labels are Dynamic
Swift - 3.0
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var height:CGFloat = CGFloat()
if indexPath.row == 0 {
height = 80
}
else if indexPath.row == 1 {
height = self.view.frame.size.height - 80 - 44 - 64
print(height)
}
return height
}

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
}

Resources