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)
Related
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
}
}
So let me start by explaining the view hierarchy.
I'm using a normal UITableView, inside the UITableViewCell there is a collectionView which is dynamic in height.
How I'm setting the datasource and delegate for UICollectionView at the willDisplay cell of UITableView:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
guard let collectionCell = cell as? movieListingTableViewCell else { return }
collectionCell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
collectionCell.selectionStyle = .none
}
}
The collectionView has the top, bottom, leading, trailing and height constraints.
Then I'm using the height constraint and setting it to the collectionView height as so in the UITableView cell subclass,
override func layoutSubviews() {
timingCollectionHeight.constant = timingCollection.collectionViewLayout.collectionViewContentSize.height
}
What happens is that my cells are not appearing properly as in there are blank spaces. For eg, if there is one UITableView cell which has a collectionView with a large height, other UITableView cells also somehow use the same height. After scrolling through the table the cells appear correctly.
Swift Example
First, get an outlet of your collectionViewHeightConstraint in your tableViewCell Class as shown below:-
#IBOutlet weak var collectionViewHeight: NSLayoutConstraint!
Then inside viewDidLoad() method of your ViewController Class add below code for dynamic height of tableViewCell:-
myTableView.estimatedRowHeight = 100; // Default tableViewCell height
myTableView.rowHeight = UITableViewAutomaticDimension;
Then inside your tableView cellForRowAtindexPath delegate add below code to update the height of tableView Cell as per CollectionView height:-
cell.frame = tableView.bounds; // cell of myTableView
cell.layoutIfNeeded()
cell.myCollectionView.reloadData()
cell.collectionViewHeight.constant = cell.myCollectionView.collectionViewLayout.collectionViewContentSize.height;
Hope this helps.
Happy Coding :)
Another approach:
After setting UITableView cell height to be dynamic.
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
Create a subclass for collectionView and override intrinsicContentSize.
class DynamicCollectionView: UICollectionView {
override func layoutSubviews() {
super.layoutSubviews()
if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
return collectionViewLayout.collectionViewContentSize
}
}
In Interface builder change the class of your collectionView to DynamicCollectionView (subclass UICollectionView).
Set estimated cell size of UICollectionViewFlowLayout.
flowLayout.estimatedItemSize = CGSize(width: 1,height: 1)
I've created a custom UITableViewCell with an xib file. In there I have placed several labels and views relative to the width of the contentView. Unfortunately the contentView.bounds.width property stays always the same no matter which device is selected.
My ViewController is also loaded from nib:
class ViewController: UIViewController {
#IBOutlet var tableView: UITableView!
init(title: String) {
super.init(nibName: "ViewController", bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerNib(UINib(nibName: "CustomTableViewCell", bundle: nil), forCellReuseIdentifier: "CustomTableViewCell")
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CustomTableViewCell", forIndexPath: indexPath) as! CustomTableViewCell
return cell
}
}
In the CustomTableViewCell class I print out the width property of the content view:
override func awakeFromNib() {
super.awakeFromNib()
print("self.contentView.bounds.width: \(self.contentView.bounds.width)")
}
This always prints out the width set in the Interface Builder, even though it should follow the width of the tableView which uses AutoLayout:
Trying to set the cell's frame before returning didn't work:
cell.frame = CGRectMake(0, 0, self.tableView.frame.size.width, 71)
If you are using autolayout then count your width in cellforrowAtindexpath and you will get proper width. awakeFromNib gets called before your cell get autolayout so it is giving same width as set in interface builder.
Second thing if you are using autolayout then setting frame has no meaning. if you want to change height or width then you should take outlet of your constraint (height or width) and you can change it's constant to desired value!
If you want to change height only then you can play with heightForRowAtIndexPath with if else which return desire height as per condition!
Swift 4+
You can use following code, it will set the frame of XIB cell and you can adjust content also with help of XIB height and width.
let cellRect = CGRect(x: 0, y: 0, width: postTableView.frame.size.width, height: imageHeight)
cell?.frame = cellRect
Hope it will work
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
I'm creating a screen, imagine the facebook profile screen.. A tableview and the first cell is an image. The goal is that this image maintains an aspect ratio, so with different screen sizes I don't have to worry about sizing it..
I can't get this to work with any view, even if I set specific constraints, the cell won't set the correct height..
I have a little project showing constraints, in this link..
My VC Code:
class ViewController: UITableViewController{
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.dataSource = self
self.tableView.delegate = self
self.tableView.estimatedRowHeight = 100
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.reloadData()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
return cell
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
}
My constrains:
If the height of the table view cell is basically a function of the table view's width, it might be easier to implement the heightForRowAtIndexPath delegate method to return the width of the table view multiplied by the ratio you want. That would take constraints out of the picture entirely, and probably be more efficient.
This is what you should do:
Insert a table view header view
Update the frame height keep the desired aspect ratio in the viewWillLayoutSubviews function.
class ViewController: UITableViewController {
#IBOutlet weak var headerView: UIView!
override func viewWillLayoutSubviews() {
var frame = headerView.frame;
// Check and see if the aspect ratio of the frame
// of the header view is the desired aspect ratio.
if frame.size.height != frame.width * 0.5 {
// If it is not, update the frame
frame.size.height = frame.width * 0.5;
headerView.frame = frame;
// reset the header view
self.tableView.tableHeaderView = headerView
}
}
}