set dynamic height of UITableViewCell inside UITableView using UILabel text - uitableview

Hello guys i have a UITableView in which i have a UITableViewCell which contains UILabel for displaying title and another UILabel for showing description. The Height of UITableViewCell is calculated based on the text of title label and description label.
Following is UITableView method to return height of cell in which i am calculating height of cell based on the text of name and description field.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var height: CGFloat = 60
if empCornerSC.selectedSegmentIndex == 0{
let kra = kraList[indexPath.row]
let maxSize = CGSize(width: 200 , height: 1000)
let nameLabelSize = rectForText(text: kra.kraName!, font: 16, maxSize: maxSize)
let descriptionLabel = rectForText(text: kra.kraDescription!, font: 14, maxSize: maxSize)
height = nameLabelSize.height + descriptionLabel.height
height = height + 20
}
return height
}
Method to calculate height based on text and font, I got this method from Youtube Lets Build That App
func rectForText(text: String, font: CGFloat, maxSize: CGSize) -> CGRect {
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
return NSString(string: text).boundingRect(with: maxSize, options: options, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: font)], context: nil)
}
i am able to get dynamic size for my UITableViewCell but it is inconsistent check the screenshot
as you can see in the image, if the label and description text are large the cell height is large and when the content of lebel are less then the size is also less. I want the cell height related to the size of content. Please help me. Thank you in advance.

Change let maxSize = CGSize(width: 200 , height: 1000) to let maxSize = CGSize(width: labelWidth , height: 1000)
labelWidth should be the maximum width allowed for a particular label. You can use something like [[UIScreen mainScreen] bounds].size.width - [XX](40/50 etc. based on your constraints). In this case 200 seems to be very less as seen in the screenshot attached

Related

Calculating height of UICollectionViewCell with text only

trying to calculate height of a cell with specified width and cannot make it right. Here is a snippet. There are two columns specified by the custom layout which knows the column width.
let cell = TextNoteCell2.loadFromNib()
var frame = cell.frame
frame.size.width = columnWidth // 187.5
frame.size.height = 0 // it does not work either without this line.
cell.frame = frame
cell.update(text: note.text)
cell.contentView.layoutIfNeeded()
let size = cell.contentView.systemLayoutSizeFitting(CGSize(width: columnWidth, height: 0)) // 251.5 x 52.5
print(cell) // 187.5 x 0
return size.height
Both size and cell.frame are incorrect.
Cell has a text label inside with 16px margins on each label edge.
Thank you in advance.
To calculate the size for a UILabel to fully display the given text, i would add a helper as below,
extension UILabel {
public static func estimatedSize(_ text: String, targetSize: CGSize = .zero) -> CGSize {
let label = UILabel(frame: .zero)
label.numberOfLines = 0
label.text = text
return label.sizeThatFits(targetSize)
}
}
Now that you know how much size is required for your text, you can calculate the cell size by adding the margins you specified in the cell i.e 16.0 on each side so, the calculation should be as below,
let intrinsicMargin: CGFloat = 16.0 + 16.0
let targetWidth: CGFloat = 187.0 - intrinsicMargin
let labelSize = UILabel.estimatedSize(note.text, targetSize: CGSize(width: targetWidth, height: 0))
let cellSize = CGSize(width: labelSize.width + intrinsicMargin, height: labelSize.height + intrinsicMargin)
Hope you will get the required results. One more improvement would be to calculate the width based on the screen size and number of columns instead of hard coded 187.0
That cell you are loading from a nib has no view to be placed in, so it has an incorrect frame.
You need to either manually add it to a view, then measure it, or you'll need to dequeu it from the collectionView so it's already within a container view
For Swift 4.2 updated answer is to handle height and width of uicollectionview Cell on the basis of uilabel text
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
let size = (self.FILTERTitles[indexPath.row] as NSString).size(withAttributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 14.0)])
return CGSize(width: size.width + 38.0, height: size.height + 25.0)
}

Making a UILabel fit nicely into a Collection View Cell

I have collection view cells with equal width and height in a flow layout. They contain a single UILabel, which numberOfLines property is set to 0. The constraints of the label are:
The cell is made circular:
cell.layer.cornerRadius = cell.frame.width / 2
cell.clipsToBounds = true
I increase the size of each cell based on the label's text size. However, it's width and height can't be greater than 150. Here is how I determine the size of each cell:
private func estimatedFrameForText(text: String) -> CGRect {
let size = CGSize(width: 100, height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
return NSString(string: text).boundingRect(with: size, options: options, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 17)], context: nil)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let hashLabelText = textArray[indexPath.item]
let textSize = estimatedFrameForText(text: hashLabelText)
let width = min(150, textSize.width + 20)
let height = width
return CGSize(width: width, height: height)
}
With this I get the following result (I'm showing the part where the cells' width and height are equal to 150).:
As you can see, when the cell hits its maximum possible size and the text of the labels continues to increase, at some point, the text gets off the visible part of the cells. I understand why this happens (the layout debugger shows this clearly), however I can't find a solution to the problem, yet.
What I want is that the edges of the label remain visible no matter what is the size of the cell. The text's tail can be truncated if the cell reaches its maximum size, but the text continues to increase.
I've tried to increase the space between the label and the bounds of the cells but that affects how the text looks in the smallest cells. I've also tried to change the minimum font scale of the label, but again, without a success.
You have to use UIEdgeInsets for this, Create a class for UILabel:
import UIKit
class UILabelDraw: UILabel {
override func drawText(in rect: CGRect) {
let insets: UIEdgeInsets = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0)
super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
}
}
Use this class as a Label class like below:
Output of UIEdgeInsets is below:

How to change detailTextLabel height programmatically

I programmed an array into a tableView. When the cell utilizes more than one line for the detailTextLabel, the space in between the lines is small. I would like to know if there is any way to increase this height programmatically? Here is sample code I am using for the array.
cell.textLabel?.text = self.filtered[indexPath.row].coptic
cell.detailTextLabel?.text = self.filtered[indexPath.row].english
cell.textLabel?.font = UIFont(name:"CS Avva Shenouda", size:30)
cell.detailTextLabel?.font = UIFont(name: "Constantia", size:25)
cell.textLabel?.numberOfLines = 0
cell.detailTextLabel?.numberOfLines = 0
cell.detailTextLabel?.textColor = UIColor.darkGray
return cell
I'm just putting my logic, not whole code.
You can get height of string by below code
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
return ceil(boundingBox.height)
}
Change cellForRowAtIndexPath Method
cell.detailTextLabel?.numberOfLines = 0
let height = height(withConstrainedWidth:200, font:YourFont) // change width and font as per your requirement
cell.detailTextLabel?.frame = CGRect(x: cell.detailTextLabel?.frame.origin.x, y: cell.detailTextLabel?.frame.origin.y, width: cell.detailTextLabel?.frame.size.width, height: height)
You can manage cell height according to detailTextLabel height
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return 50 // You should put your code or logic that dynamic height based on heigh of label.
}
The table view needs to have an estimatedRowHeight and the tableView height as UITableViewAutomaticDimension.

How to resize table cell based on textview?

I have a UITextView in a custom UITableViewCell. The textview delegate is assigned in the tableviewcell custom class. Textview scrolling is disabled. Text loads into each textview and is multiline. But the text is always clipped because the cell height doesn't change.
I have the following in viewDidLoad of the tableview controller:
tableView.estimatedRowHeight = 56.0
tableView.rowHeight = UITableViewAutomaticDimension
Any idea why this isn't working?
Try my answer its work perfectly.
var record : NSArray = NSArray()
var hight: CGFloat = 0.0
Put this code in viewDidLoad()
record = ["I have a UITextView in a custom UITableViewCell. The textview delegate is assigned in the tableviewcell custom class." ,"Textview scrolling is disabled. Text loads into each textview and is multiline. But the text is always clipped because the cell height doesn't change.","I have the following in viewDidLoad of the tableview controller:"," have a UITextView in a custom UITableViewCell. The textview delegate is assigned in the tableviewcell custom class.","Textview scrolling is disabled. Text loads into each textview and is multiline. But the text is always clipped because the cell height doesn't change.","I have the following in viewDidLoad of the tableview controller:","i just give you one link at put place i use label and you can now use your textview and give same constrain that i give in that link and try it so your problem will be solve","I have the following in viewDidLoad of the tableview controller:"];
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return record.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Textviewcell", forIndexPath: indexPath)
let textview: UITextView = (cell.viewWithTag(5) as! UITextView)
textview.text = record.objectAtIndex(indexPath.row) as? String
return cell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
// 7.1>
hight = self.findHeightForText(self.record.objectAtIndex(indexPath.row) as! String, havingWidth: self.view.frame.size.width - 10, andFont: UIFont.systemFontOfSize(14.0)).height
return 44 + hight
}
func findHeightForText(text: String, havingWidth widthValue: CGFloat, andFont font: UIFont) -> CGSize {
var size = CGSizeZero
if text.isEmpty == false {
let frame = text.boundingRectWithSize(CGSizeMake(widthValue, CGFloat.max), options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
size = CGSizeMake(frame.size.width, ceil(frame.size.height))
}
return size
}
In Swift 3.0
func findHeightForText(text: String, havingWidth widthValue: CGFloat, andFont font: UIFont) -> CGSize {
var size = CGSize.zero
if text.isEmpty == false {
let frame = text.boundingRect(with: CGSize(width: widthValue, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
size = CGSize(width: frame.size.width, height: ceil(frame.size.height))//CGSizeMake(frame.size.width, ceil(frame.size.height))
}
return size
}
Here are some screen shot .Storyboard
Runtime tableview with UITextView
You should use the delegate method
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
to set the height for the row. Then, inside that method, return the height that the cell should be by calculating the height of the textview which will occupy the space. You should use the function:
-(CGFloat)heightForTextViewRectWithWidth:(CGFloat)width andText:(NSString *)text withBuffer:(float)buffer
{
UIFont * font = [UIFont fontWithName:#"YourFontName" size:15.0f]; // Replace with your font name and size
NSDictionary *attributes = #{NSFontAttributeName: font};
// this returns us the size of the text for a rect but assumes 0, 0 origin, width is the width of that your text box will occupy.
// Ex. If you text box has padding of 12 both trailing and leading to the cell, then width should be the cell's width minus 24.
CGRect rect = [text boundingRectWithSize:CGSizeMake(width, MAXFLOAT)
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes:attributes
context:nil];
// return height of rect
return rect.size.height;
}
This will determine the rect that your text view will occupy in the cell. The height that is returned is equal to the height of the text view, so if you want the cell to be taller than the text box, add that padding.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString * yourText = commentDict.text; // or however you are getting the text
float widthPadding = 24.0f;
float heightPadding = 16.0f;
float height = [self heightForTextViewRectWithWidth: (tableView.frame.size.width - widthPadding) andText:yourText];
return height + heightPadding;
}
Hope this helped!

How to calculate height of a String?

I am trying to adjust the cell height resize to fit the UILabel text, but it is not working..
var mySize = CGFloat()
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! cellView
cell.myLabel.text = self.items[indexPath.item]
cell.myLabel.bounds.size.height = self.mySize
cell.backgroundColor = UIColor.yellowColor()
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
// handle tap events
print("You selected cell #\(indexPath.item)!")
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
func heightForLabel(text:String, font:UIFont, width:CGFloat) -> CGFloat
{
let label:UILabel = UILabel(frame: CGRectMake(0, 0, width, CGFloat.max))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.ByWordWrapping
label.font = font
label.text = items[indexPath.row]
label.sizeToFit()
return label.frame.height
}
let font = UIFont(name: "Helvetica Neue", size: 30)
let detailHeight = heightForLabel(items[indexPath.row], font: font!, width: UIScreen.mainScreen().bounds.size.width)
self.mySize = detailHeight
return CGSizeMake(UIScreen.mainScreen().bounds.size.width, 358 + detailHeight)
}
Any suggestions what to do here? Should i do it another way? Please, I need help.. The problem is that the UILabel text is set in the cellForItemAtIndexPath, and items is an array for strings.
This is my project file, if someone watch to take a look at it:
http://www.filedropper.com/test_37
Why not try this in ObjC
[text boundingRectWithSize:CGSizeMake(maxWidth, maxHeight)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:nil context:nil]
This will give CGRect. Get the height from it. set font size etc in attributes parameter.
UPDATE
In place of this
let detailHeight = heightForLabel(items[indexPath.row], font: font!, width: UIScreen.mainScreen().bounds.size.width)
Use this
let height = items[indexPath.row].boundingRectWithSize(CGSizeMake(CGFloat.max,UIScreen.mainScreen().bounds.size.width), options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName: font!], context: nil).size.height
Hope this helps
You can do it like that but I have implemented it in different way. Here is the sample of code you can do it.
let desiredWidth: CGFloat = tableView.bounds.size.width
let label: UILabel = UILabel()
let desiredString = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged."
label.text = desiredString
label.numberOfLines = 0;
label.lineBreakMode = NSLineBreakMode.ByWordWrapping
let size: CGSize = label.sizeThatFits(CGSizeMake(desiredWidth, CGFloat.max))
print("Label height you can set to your cell: \(size.height)")
Crate an String extension
extension String {
func heightOfString(usingFont font: UIFont) -> CGFloat {
let fontAttributes = [NSFontAttributeName: font]
let size = self.size(attributes: fontAttributes)
return size.height
}
}
get the height of the string as follows
let str = "Hello world"
let strHgt = str.heightOfString(usingFont: UIFont.systemFont(ofSize: 12))
I create this method for getting height of a label. You need to provide label's static Width and label's font
func dynamicHeight(font: UIFont, width: CGFloat) -> CGFloat{
let calString = NSString(string: self)
let textSize = calString.boundingRectWithSize(CGSizeMake(width, CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin|NSStringDrawingOptions.UsesFontLeading, attributes: [NSFontAttributeName: font], context: nil)
return textSize.height
}
Try this...
NSString *yourText = #"Your string";
CGSize lableWidth = CGSizeMake(300, CGFLOAT_MAX);
CGSize requiredSize = [yourText sizeWithFont:[UIFont fontWithName:#"CALIBRI" size:17] constrainedToSize:lableWidth lineBreakMode:NSLineBreakByWordWrapping];
int calculatedHeight = requiredSize.height;
return (float)calculatedHeight;
I took a look at your code, and I was able to solve it.
Firstly, on line 71 in your ViewController class:
let height = items[indexPath.row].boundingRectWithSize(CGSizeMake(CGFloat.max, UIScreen.mainScreen().bounds.size.width), options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName: font!], context: nil).size.height
You accidentally set CGFloat.max as width and the width as height. It should be:
CGSizeMake(UIScreen.mainScreen().bounds.size.width, CGFloat.max)
I'd say it's better practice to use the width of the view that the cell is directly contained in (the collectionView), but that's just my personal opinion.
Secondly, you need to enable AutoLayout. Go to your Main.storyboard file and make sure Use Auto Layout is selected.
Now you need to add constraints. (You can read more about AutoLayout and constraints here)
There are different ways to add constraints. The easiest way is to control click a UI element and drag the mouse to the element you want to set a constraint to.
You need to add the following constraints for your cell:
ImageView.top = cell.top
ImageView.leading = cell.leading
ImageView.trailing = cell.trailing
ImageView.bottom = MyLabel.top + 8 (your padding)
MyLabel.leading = cell.leading
MyLabel.trailing = cell.trailing
MyLabel.bottom = cell.bottom
And these for your CollectionView
CollectionView.top = view.top
CollectionView.leading = view.leading
CollectionView.trailing = view.trailing
CollectionView.bottom = view.bottom
I've attached the project, modified with AutoLayout here below.
Modified project
Edit:
Approach 2 - without AutoLayout.
This could also be achieved without using AutoLayout by manually updating the cell's label height in collectionView:willDisplayCell:. I'm sure there are better alternatives, I'd personally try AutoResizingMasks before this approach.
Project without AutoLayout
extension String{
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
return ceil(boundingBox.height)
}}

Resources