UITableViewCell minimum height with automatically resizing cells - ios

I am using automatically resizing cells in my project. I want to keep the cells at a minimum height, but I cannot set a minimum height constraint for my cells in my Content View (as it is greyed out in the Add New Constraints view:
Is there anyway to add minimum height constraints for my cells?

When UITableViewAutomaticDimension is enabled the system calls UITableViewCell's systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority: method to calculate the cell height, so you can subclass UITableViewCell and override this method to force a minimum height for the cell, in this way
class MinHeightTableViewCell: UITableViewCell {
var minHeight: CGFloat?
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
let size = super.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: horizontalFittingPriority, verticalFittingPriority: verticalFittingPriority)
guard let minHeight = minHeight else { return size }
return CGSize(width: size.width, height: max(size.height, minHeight))
}
}
And then
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "minHeightCell", for: indexPath) as! MinHeightTableViewCell
cell.minHeight = 62 // change with your desidered min height
cell.textLabel?.text = "some text"
return cell
}

set a height constraint as height >= 60.0 No need to do anything

I added this constraint to my cell but in code (swift)
contentView.heightAnchor.constraint(equalToConstant: 80).isActive = true
If your cell view needs more space:
contentView.heightAnchor.constraint(greaterThanOrEqualToConstant: 80).isActive = true

Here's code used in viewDidLoad. cell is the UITableViewCell. Notice the relatedBy parameter is >=. 44 is my minimum height...
[cell.contentView addConstraint: [NSLayoutConstraint constraintWithItem: cell.contentView attribute: NSLayoutAttributeHeight relatedBy: NSLayoutRelationGreaterThanOrEqual toItem: nil attribute: NSLayoutAttributeNotAnAttribute multiplier: 1 constant: 44]];

You can makes your cells as dynamic height by returning the UITableViewAutomaticDimension in height calculation methods of tableview. But for this your cell design constraints should be like the below one
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewAutomaticDimension;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewAutomaticDimension;
}
Note: Todo Title is the static height. And description will be dynamic height. So description does not own the height constrain. Important thing is you need set a number of lines as 0 for description label

simple added height constraint with minimum height you want and then change it relation to "Greater then Equal to". look highlighted section of screen short.

Follow these steps, to set automatic dimension for cell with some minimum height.
Assign and implement tableview dataSource and delegate
Assign UITableViewAutomaticDimension to rowHeight & estimatedRowHeight
Implement delegate/dataSource methods (i.e. heightForRowAt and return a value UITableViewAutomaticDimension to it)
-
Objective C:
// in ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property IBOutlet UITableView * table;
#end
// in ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.table.dataSource = self;
self.table.delegate = self;
self.table.rowHeight = UITableViewAutomaticDimension;
self.table.estimatedRowHeight = UITableViewAutomaticDimension;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
Swift:
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Don't forget to set dataSource and delegate for table
table.dataSource = self
table.delegate = self
// Set automatic dimensions for row height
table.rowHeight = UITableViewAutomaticDimension
table.estimatedRowHeight = UITableViewAutomaticDimension
}
// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
For label instance in UITableviewCell
Set number of lines = 0 (& line break mode = truncate tail)
Set all constraints (top, bottom, right left) with respect to its superview/ cell container.
Note: Set minimum height for label, if you want minimum vertical area covered by label, even if there is no data.

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)

UITableViewCell not able to adjust height based on the dynamic CollectionView it contains

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)

Adjust UILabel Dynamically Within UITableViewCell?

I would like to adjust the number of lines in a UILabel dynamically. For example, there could be a long string or a short string. If there are less than 10 characters, there should be one line. If there are 10-20 characters, it should be two lines. This UILabel is inside of a UITableViewCell. Currently, if there is a long string, then the UILabel just shows "..." wherever it runs out of space, so the string is cut off. How can I prevent this from happening?
Try:
nameOfLabel.numberOfLines = 0
0 is supposed to be dynamic.
Constaints from Top Left Right Buttom of label and label.numberOfLine = 0 make it dynamically resizable according to dynamic content.
First set numberOfLines to 0 for that UILabel from Storyboard
Than set estimated height for your UITableView
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 108.0
Than Add heightForRowAt method
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Hope it Help!
Assign and implement tableview dataSource and delegate
Assign UITableViewAutomaticDimension to rowHeight & estimatedRowHeight
Implement delegate/dataSource methods (i.e. heightForRowAt and return a value UITableViewAutomaticDimension to it)
-
Objective C:
// in ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property IBOutlet UITableView * table;
#end
// in ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.table.dataSource = self;
self.table.delegate = self;
self.table.rowHeight = UITableViewAutomaticDimension;
self.table.estimatedRowHeight = UITableViewAutomaticDimension;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
Swift:
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Don't forget to set dataSource and delegate for table
table.dataSource = self
table.delegate = self
// Set automatic dimensions for row height
table.rowHeight = UITableViewAutomaticDimension
table.estimatedRowHeight = UITableViewAutomaticDimension
}
// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
For label instance in UITableviewCell
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.
Note: If you've more than one labels (UIElements) with dynamic length, which should be adjusted according to its content size: Adjust 'Content Hugging and Compression Resistance Priority` for labels which you want to expand/compress with higher priority.
You need to give constraints to tableview cell's label leading, trailing, top and bottom like below...
Also set label's number of lines to zero and line breaking mode to word wrap.
And in your main view controller just add this line...
self.tableView.estimatedRowHeight = UITableViewAutomaticDimension
The above line automatically calculates the cell height as per its content.

UITableViewCell: get the exact width of UILabel in cell

I am having the problem when getting the correct width of the label in custom cell.
This is my cell in storyboard:
When the table is created, I understand that when cellForRowAtIndexPath is called, the cell is just created and not added to UITableView yet, so the width of the label is 304px. This causes the result like this:
After scrolling, the cell has been added to the UITableView so it shows correctly, with the width of the label is 164px.
Is there any way to know the exact width of cell/label before it is added to UITableView?
The constraints on the label: Leading, Trailing, Top and Height
Below is source code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: ArticleCell = tableView.dequeueReusableCellWithIdentifier("articleCell", forIndexPath: indexPath) as! ArticleCell
let item = array.objectAtIndex(indexPath.row) as! Article
cell.showDataForArticle(item, dateFormatter: dateFormatter)
return cell
}
func showDataForArticle(article: Article, dateFormatter: NSDateFormatter) {
self.titleLbl.text = article.articleTitle
titleHeightConstraint.constant = titleLbl.requiredHeight() <--- where problem happens
self.thumbnailImgView.imageLink(article.articleThumbnailLink!)
self.authorLbl.text = article.articleCreator
self.dateLbl.text = dateFormatter.stringFromDate(article.articlePubDate!)
}
extension UILabel {
func requiredHeight() -> CGFloat {
let frame = CGRectMake(0, 0, self.frame.size.width, CGFloat.max)
let label: UILabel = UILabel(frame: frame)
label.numberOfLines = 0
label.lineBreakMode = .ByWordWrapping
label.font = self.font
label.text = self.text
label.sizeToFit()
return label.frame.size.height
}
As far as trying to get the width of the label have you tried something alone the lines of
self.YourLabelName.bounds.width
You can have a label that would adjust its height based on the text it has to show using auto layouts.
1. For this to work you should not set static height for your cell, just add the below two delegates,
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 44;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
2. You should set top to bottom constraint for your cell,
In your case your header label should have TopSpace to Cell, LeadingSpace to ImageView, TrailingSpace to Cell (with standard spacing) and BottomSpace to AuthorLabel(with standard spacing).
3. The label you want with dynamic height as per text should have set the number of lines to 0 and LineBreakMode to Word Wrap.
Thats it, also find this links similar to your case,
Dynamic UITableViewCell content does not expand cell
Dynamically increase height of UILabel & TableView Cell?
Try calling titleLbl.requiredHeight() in func tableView(_ tableView: UITableView,
willDisplayCell cell: UITableViewCell,
forRowAtIndexPath indexPath: NSIndexPath) method

How to support auto resizing UITableViewCell with a UILabel in it

I am trying to figure out how get the correct height for my tableview cell that contains a uitextview so that all the content in the uitextview will be display. The textview is not editable and not scrollable, which means that the string for the uitextview is known. I tried simply returning the height of the tableview cell that is in the storyboard in heightForRowAtIndexPath but that cuts off the content if it is too large. I also tried calculating the height for the textView in heightForRowAtIndexPath but it throws an error at runtime:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let cellInfo = UserCellsArray[indexPath.section]
switch cellInfo {
case .userInfo: return 104
case .ubio:
let cell = tableView.dequeueReusableCellWithIdentifier(cellInfo.description, forIndexPath: indexPath) as! UserProfileTableViewCell //error
let oldHeight = cell.userBio.frame.size.height
cell.userBio.text = self.bio
var bodyframe = cell.userBio.frame
bodyframe.size = cell.userBio.contentSize
let newHeight = cell.userBio.contentSize.height
return tableView.rowHeight - oldHeight + newHeight
}
}
Here is what you need to do:
Need to use auto layout in your cell and set up appropriate constraints for UILabel. Since we want to resize the UILabel to fit the text set to it, we will set the number of lines value to 0. And then set the leading, trailing, bottom and top constraints to the UILabel. Please see screenshots here:
In your Table View controller > viewDidLoad call these two lines:
tableView.estimatedRowHeight = 50.0 //Give an approximation here
tableView.rowHeight = UITableViewAutomaticDimension
and also implement delegate method:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat{
return UITableViewAutomaticDimension
}

Resources