Tableview height based on its content - uitableview

I want to create table view's that are constrained to their super view's with a top,left, and right margin, but a lessThanOrEqualTo bottom margin. This way if the tale view's content is taller than its super view it will be constrained to the bottom margin and scroll. But if its e.g. only 3 cells, and the cells have their own internally consistent constraints, the tableview will be e.g. 200 pts tall and their will be space between it and the super view bottom margin. So far when trying this, if i don't specify the bottom constraint or set it as lessThanOrEqual to I see an ambiguous layout warning in the visual debugger.

Tableview height based on its content with UITableViewAutomaticDimension
//make bottom margin constraint #IBOutlet for tableView
#IBOutlet weak var bottomConstraint: NSLayoutConstraint!
#IBOutlet weak var tableView: UITableView!
var rowCount = 3
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
make outlet connection to a tableView bottom constraint
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return CGFloat.leastNormalMagnitude
}
var height: [CGFloat] = [0]
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let indexRowNo = indexPath.row
//will give you cell height for indexPath
let frame = tableView.rectForRow(at: indexPath)
//if cell height already exists in height update value else append
if self.height.count-1 >= indexRowNo{
height.remove(at: indexRowNo)
height.insert(frame.size.height, at: indexRowNo)
}else{
height.append(frame.size.height)
}
if indexPath.row == (tableView.indexPathsForVisibleRows?.last)?.row {
//remove old cell heights if now tableView has less noOfCell after reloadData
if self.height.count >= rowCount{
for _ in rowCount..<self.height.count{
self.height.removeLast()
}
}
}
// total Height for tableView
let totalHeight: CGFloat = height.reduce(0, +)
//e.g. - less/greater ThanOrEqualTo bottom margin conditions
let viewTotalHeight = self.view.frame.height - 80
if totalHeight <= viewTotalHeight{
let value = (self.view.frame.height) - (totalHeight + 10)
self.bottomConstraint.constant = value
self.tableView.isScrollEnabled = false
}else{
self.bottomConstraint.constant = 0
self.tableView.isScrollEnabled = true
}
}

Related

How to dynamically resize TableView Cell Height and TableView Height

I have a tableview with two cells, The content of the tableview cell will change and will be dynamic, so I want to dynamically resize the tableview cell and according to the tableview cell height I want to calculate the height of tableview.
My tableview is taking an extra space while rendering the cells.
Can anyone help me with the approach on how to set the tableview height according to the tableview cell height
class Recent_WH_acitivityCell: UITableViewCell {
#IBOutlet weak var wh_activity_tableView: UITableView!
#IBOutlet weak var wh_activity_table_ht: NSLayoutConstraint!
let cells = 2
var maxHeight: CGFloat = UIScreen.main.bounds.size.height
override func awakeFromNib() {
super.awakeFromNib()
wh_activity_tableView.estimatedRowHeight = 100
wh_activity_tableView.rowHeight = UITableViewAutomaticDimension
}
func setData()
{
wh_activity_tableView.reloadData()
self.layoutIfNeeded()
}
override var intrinsicContentSize: CGSize
{
let height = min(self.contentView.frame.height, maxHeight)
return CGSize(width: self.contentView.frame.width, height: height)
}
override func layoutSubviews() {
super.layoutSubviews()
self.contentView.frame = UIEdgeInsetsInsetRect(contentView.frame, UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0))
}}
extension Recent_WH_acitivityCell : UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "recent_wh_data_cell", for: indexPath) as! Recent_WH_Data_cell
return cell
}
}
Here is the Screenshot
Please look into the highlighted part.
You can implement a self sizing UITableView like this:
class SelfSizingTableView: UITableView {
override var intrinsicContentSize: CGSize {
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
}
override var contentSize: CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}
}
Make sure to set up the cell's constraints correctly (full constraints from the top to the bottom of the cell) and enable self-sizing cells in your view controller like this:
#IBOutlet weak var tableView: UITableView! {
didSet {
tableView.estimatedRowHeight = 44
tableView.rowHeight = UITableView.automaticDimension
}
}
Assign constraint align top and bottom to parent view. and then use one call as header cell like "Sell your stock" and use cell as normal cell under section, So you can get proper output as your requirement.
Also assign height constraint and IBOutlet it and calculate number of cell * cell height + header height = tableview height constraint
And then set accordingly UIScrollview contentsize.
For example one cell height is 80 and number of cell 5 then scrollview content size is = other item height + header height + (80 * 5)
tableview height constraint is = header height + (80 * 5)

UICollectionView inside UITableViewCell how to adjust tableview cell height based on screen size

I have the following layout
UITableView
UITableViewCell (class: CategoryCell)
Label
Button
UICollectionView
UICollectionViewCell (class: ItemCell)
UIImageView
UILabel
I am trying to make UICollectionView to always show 3 items no matter the screen size. I was able to get items to be shown based on screen width and was able to set the height. However, the height of table view does not change from inside CategoryCell. Here is the code:
// CategoryCell.swift
func awakeFromNib() {
super.awakeFromNib()
// Calculate width and height based on screen width
let screenWidth = UIScreen.main.bounds.width
let itemWidth = screenWidth / 3.0
let itemHeight = itemWidth / 0.75 // 3:4 aspect ratio
// Change height of table view cell
self.bounds.size.height = self.bounds.size.height - collectionView.bounds.size.height + itemHeight
// This is the line does nothing
// It should remove collectionView height from tableView (height of collectionView is set through autolayout) then add he new height to it.
// Set collection view height to equal to item height
collectionView.bounds.size.height = itemHeight
// set item height
let layout = collectionViewProducts.collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = CGSize(width: itemWidth, height: itemHeight - 1.0)
// -1.0 because item/cell height needs to be SMALLER than collection view height
}
How can I change table view cell height from inside the cell class itself? My only guess is that I should not be doing these operations inside awakeFromNib but I am not able to find another function to call these from.
EDIT: I am using RxSwift, so my data source code is the following:
let observable = Observable.just(data)
observable.bindTo(tableView.rx.items(cellIdentifier: "CategoryCell", cellType: CategoryCell.self)) { (row, element, cell) in
cell.setCategory(category: element)
}.disposed(by: disposeBag)
tableView.rx.setDelegate(self).disposed(by: disposeBag)
You could implement UITableViewDelegate's heightForRowAt and return a value based on a variable. And you can set the variable wherever you do your UICollectionView itemHeight calculation. So, when you are done with the calculation, you should be able to do a table view data reload and the layout should update using the new itemHeight value.
I have not tested the code but the above should work. If you run into any issues, or I've misunderstood your requirements somehow, do let me know.
Provide the constraints to the collection view and make collectionviewcell outlet in tableviewcell page as well as collectionviewheight constraint outlet in the same tableviewcell page.
Like this in tableviewcell page:
class HomeServiceTableCell: UITableViewCell {
#IBOutlet weak var dealsCollectionView: UICollectionView!
#IBOutlet weak var dealsCollectionHeight: NSLayoutConstraint!
#IBOutlet weak var dealsImage: UIImageView!
// like this in the main page cellforRowAt indexPath function
func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = homeServiceTable.dequeueReusableCell ( withIdentifier: "homeServiceTableCell6" ) as! homeServiceTableCell6
cell.dealsImage.image = UIImage(named: "deals.png")
cell.dealsCollectionView.tag = indexPath.row
cell.dealsCollectionView.delegate = self
cell.dealsCollectionView.dataSource = self
cell.dealsCollectionView.reloadData()
cell.dealsCollectionHeight.constant = cell.dealsCollectionView.collectionViewLayout.collectionViewContentSize.height
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
// lastly add this line in viewWillAppear function
override func viewWillAppear(_ animated: Bool) {
self.homeServiceTable.layoutSubviews()
}
}
You need to provide tableview's cell height for every cell and based on that you can calculate your collectionview cell's height. for dynamic height of tableview's cell you need to implement these method.
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
or if you need fixed height then simply use these method and provide your cell height based on your calculation.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return fixedSize //e.g 100
}
try this hope it will help you.
Thank you #Prince and #Fahim. Your answers gave me an idea to move my row height logic to tableView delegate. Since, I am calculating the height based on screen width and aspect ratio. I just moved the same logic to tableView height:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let screenWidth = UIScreen.main.bounds.size.width
// 3 items + 10px gap between cells
let itemWidth = (screenWidth / 3.0) - 20
// 3:4 aspect ratio + other elements
let itemHeight = (itemWidth) / 0.75 + 110
return itemHeight
}
For now this solution works normally for me, I would like to change it so it takes into account the cell itself.
I know it is weird to ask a question in the answer but it is related to this thread, so I think it is best to ask it here. How can I get the cell object from indexPath? Using tableView.cellForRow(at: indexPath) gives me bad access.

adjusting tableView height while using self sizing cells and a non scrollable table

The problem I have is that I am using self-sizing cells, and estimated row height.
My tableview is a non scrollable table in a scrollview. I have a variable for the table height which I initially set to 0.
I am trying to update my tables height to be the right size depending on how many cells will fill it and depending on the size those cells will take up. I am trying to use tableview.contentSize.height to get the tables height and set the variable to that size.
I then recall my alignment function to reset the tables height. When I build and run the tables size isn't big enough to show all of the cells that it should.
How can I fix this?
You can do it from the following steps ->
Construct the tableview within the UIView with 0 Leading, Top, bottom, Trailing constant
Add height constraint of the UIView and define outlet of it
3. Load Data in the tableview and get tableview's ContentHeight
Update the height constraint of the UIView equal to the tableview's ContentHeight
Dont forget to call layoutIfNeeded() after updating constraint
class ViewController: UIViewController {
#IBOutlet weak var containerViewHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var nameTableView: UITableView!
var dataArrary = [String]()
override func viewDidLoad() {
super.viewDidLoad()
nameTableView.rowHeight = UITableViewAutomaticDimension
nameTableView.estimatedRowHeight = 44.0
nameTableView.tableFooterView = UIView(frame:CGRectZero)
nameTableView.tableHeaderView = UIView(frame:CGRectZero)
dataArrary = ["item1 bshfklsdflkjsdfkljsdklfjlsdkfjlksdjflksdjflkdsjflkdsjflksdjflkjdslkfjdslkjflkdsfjdsfkljsdflkjdslkfj", "item2hfdshjgfhjdsgfhjdgfhjdgfhjdgfhjgfhsdf", "item3", "item4", "item5", "item6kfjdskfljsdlkfjlksdfjlksdjfkldsjfklfjdkslf"]
}
override func viewDidAppear(animated: Bool) {
updateView()
}
func updateView() {
self.nameTableView.scrollEnabled = false
self.nameTableView.frame.size = CGSizeMake(self.view.frame.width, self.nameTableView.contentSize.height)
containerViewHeightConstraint.constant = self.nameTableView.frame.height
// assign height equal to the tableView's Frame
self.view.layoutIfNeeded()
// redraws views and subviews with updated constraint
}
}
extension ViewController:UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArrary.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.numberOfLines = 0
cell.textLabel?.text = dataArrary[indexPath.row]
return cell
}
}
It should look like this

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