Dynamic custom UITableViewCell's height based on label text length in Swift - ios

I've been trying to do a dynamic cell with label using swift to support IOS7 in heightForRowAtIndexPath but i only find objective-c code, is it possible to somebody else to help me rewriting this code into swift ?
// Fetch yourText for this row from your data source..
NSString *yourText = [yourArray objectAtIndex:indexPath.row];
CGSize lableWidth = CGSizeMake(300, CGFLOAT_MAX); // 300 is fixed width of label. You can change this value
CGSize requiredSize = [yourText sizeWithFont:[UIFont fontWithName:#"Times New Roman" size:19] constrainedToSize:lableWidth lineBreakMode:NSLineBreakByWordWrapping]; // You can put your desire font
// Here, you will have to use this requiredSize and based on that, adjust height of your cell. I have added 10 on total required height of label and it will have 5 pixels of padding on top and bottom. You can change this too.
int calculatedHeight = requiredSize.height+10;
return (float)calculatedHeight;
exactly this statement (how to convert it to swift) :
CGSize requiredSize = [yourText sizeWithFont:[UIFont fontWithName:#"Times New Roman" size:19] constrainedToSize:lableWidth lineBreakMode:NSLineBreakByWordWrapping]
i tried converting it to (but it doesn't work as the original objective-c ) :
var requiredSize: CGSize = originalString.sizeWithAttributes([NSFontAttributeName: UIFont.systemFontOfSize(19.0)])

Maybe it's possible to do it without the UILabel but this works just fine.
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 10000))
label.text = someText
label.numberOfLines = 100
label.font = UIFont(name: "Times New Roman", size: 19.0)
label.sizeToFit()
return label.frame.height + 10

Related

How to calculate TextView height base on text

I am using the code below for calculate the height of text, then set this height for UILabel and UITextView
CGSize targetSize = CGSizeMake(300, CGFLOAT_MAX);
NSString *message = #"The Internet connection appears to be offline.";
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
CGSize boundingBox = [message boundingRectWithSize:targetSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:FontOpenSanWithSize(14)}
context:context].size;
CGSize size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
// it will return size:width = 299 and size height 20
// => if I use this height and set for UILabel, it can display full content
// => if I use this height and set for UITextView, it can not display full content
It's work perfect for UILabel but for UITextView sometime it calculate wrong.
I think the problem happened because the padding (left, right) of UITextView is bigger than UILabel.
So how can I calculate the correct size of text for display in UITextView. Any help or suggestion would be great appreciated.
Like the description image below
With the same size (300), same font, same text but the UITextView display in 2 lines, but UILabel in 1 lines.
And my code for calculate height return 20, it not enough for display in 2 lines, so the UITextViewcan not display full content
The reason why I need to calculate the height of UITextView base on text because my UITextView is in a popup.
And the popup height will depend on the TextView height
There are two things you can try:
Set textView.textContainerInset = UIEdgeInsetsZero
Set textView.textContainer.lineFragmentPadding = 0
With these operations you can get rid of all the padding in the textView and when its width matches with the label's one the heights are also the same.
Here's a sample code you can place in an empty viewController and test it yourself:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSString *text = #"The internet connection appears to be offline.";
CGFloat width = 100.f;
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(20, 20, width, 300)];
textView.font = [UIFont fontWithName:#"AvenirNext-Regular" size:12.f];
textView.text = text;
[self.view addSubview:textView];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20 + width, 20, width, 300)];
label.numberOfLines = 0;
label.font = [UIFont fontWithName:#"AvenirNext-Regular" size:12.f];
label.text = text;
[self.view addSubview:label];
// Getting rid of textView's padding
textView.textContainerInset = UIEdgeInsetsZero;
textView.textContainer.lineFragmentPadding = 0;
// Setting height of textView to its contentSize.height
CGRect textViewFrame = textView.frame;
textViewFrame.size = textView.contentSize;
textView.frame = textViewFrame;
// Setting height of label accorting to it contents and width
CGRect labelFrame = label.frame;
labelFrame.size = [label sizeThatFits:CGSizeMake(width, HUGE_VALF)];
labelFrame.size.width = width;
label.frame = labelFrame;
NSLog(#"Label bounds: %#", NSStringFromCGRect(label.bounds));
NSLog(#"TextView bounds: %#", NSStringFromCGRect(textView.bounds));
// Visualizing final effect with borders
textView.layer.borderColor = [UIColor redColor].CGColor;
textView.layer.borderWidth = 1.f;
label.layer.borderColor = [UIColor greenColor].CGColor;
label.layer.borderWidth = 1.f;
}
Console output:
2016-09-01 14:29:06.118 stack39268477[943:243243] Label bounds: {{0, 0}, {100, 66}}
2016-09-01 14:29:06.119 stack39268477[943:243243] TextView bounds: {{0, 0}, {100, 66}}
You don't need to calculate Height of UITextview based on text.
Just change frame and set height like this:
textview.size.height = textview.contentSize.height;
This is easy solution. I hope this helps you.
This calculates the size of any string, whether or not you put them in a text view.
let frame = NSString(string: yourText).boundingRect(
with: CGSize(width: yourDesiredWidth, height: .infinity),
options: [.usesFontLeading, .usesLineFragmentOrigin],
attributes: [.font : yourFont],
context: nil)
let height = frame.size.height
Most of the answers here are hints into the right direction :-)
So, just to sum it all up...
UITextView uses a NSTextContainer (inside a private API _UITextContainerView) to do the real layout work.
This NSTextContainer(View) may have insets to the surrounding UITextView, which are set by UITextView's textContainerInset property.
The defaults for this insets seem to be:
top: 8
left: 0
bottom: 8
right: 0
The NSTextContainer itself may have additional left and right insets for the text itself. These insets are set in NSTextContainer's lineFragmentPadding property.
The default for this is 5.0.
As a result, when calculating the optimum frame size for a UITextView based on the boundingRect for some text inside that UITextView, we have to take all these insets into account:
CGSize reservedSpace = CGSizeMake((textView.textContainerInset.left + (2.0 * textView.textContainer.lineFragmentPadding) + textView.textContainerInset.right),
(textView.textContainerInset.top + textView.textContainerInset.bottom));
CGSize targetSize = CGSizeMake((300.0 - reservedSpace.width), CGFLOAT_MAX);
NSString* message = #"The Internet connection appears to be offline.";
NSStringDrawingContext* context = [[NSStringDrawingContext alloc] init];
CGSize boundingBox = [message boundingRectWithSize:targetSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:FontOpenSanWithSize(14)}
context:context].size;
CGSize size = CGSizeMake(ceil(boundingBox.width),
(ceil(boundingBox.height) + reservedSpace.height));
Good luck :-)
Swift 5
A classic hack I've used to do this is create a function that takes the String as a parameter. Within the function it generates a label with the width required, infinite number of lines, and a height of "too much". Then apply sizeToFit() on the label and return the height of the frame.
func calculatedHeight(for text: String, width: CGFloat) -> CGFloat {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: width,
height: .greatestFiniteMagnitude))
label.numberOfLines = 0
label.text = text
label.sizeToFit()
return label.frame.height
}
The returned height can be applied to a UITextField height anchor. Though I do recommend calculating the width dynamically based on screen size, but that's up to you.
self.textView.textContainerInset = UIEdgeInsets.zero
self.textView.textContainer.lineFragmentPadding = 0
In storyboard or xib mark textview height >=0.
If you are using text view with table view.
Calculate cell height according to content, textview will adjust it's space.

Problems through the sizeWithFont depreciated in iOS7.0 in my code. Don't understand how to replace it with boundingRectWithSize [duplicate]

This question already has answers here:
IOS 7 sizeWithFont Deprecated
(3 answers)
Closed 7 years ago.
So as I need to upgrade my app to newer iOS i came across the issue with sizeWithFont command being depreciated. Can someone please help me out on how to replace it with the new Function.
Here is my code:
// Add label if label text was set
if (nil != self.labelText) {
// Get size of label text
CGSize dims = [self.labelText sizeWithFont:self.labelFont];
// Compute label dimensions based on font metrics if size is larger than max then clip the label width
float lHeight = dims.height;
float lWidth;
if (dims.width <= (frame.size.width - 2 * margin)) {
lWidth = dims.width;
}
else {
lWidth = frame.size.width - 4 * margin;
}
// Set label properties
label.font = self.labelFont;
label.adjustsFontSizeToFitWidth = NO;
label.textAlignment = NSTextAlignmentCenter;
label.opaque = NO;
label.backgroundColor = [UIColor clearColor];
label.textColor = [UIColor whiteColor];
label.text = self.labelText;
How do I need to use the new Function to get the size of the label text?
Use boundingRectWithSize as follows :
CGSize sizeT = CGRectIntegral([yourstring boundingRectWithSize:CGSizeMake(yourpreferedwidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil]).size;
or you can use sizeThatFits
CGSize maximumLabelSize = CGSizeMake(310, 9999);
CGSize expectSize = [yourlabel sizeThatFits:maximumLabelSize];
For more see this answer : https://stackoverflow.com/a/19518480/3202193
Use sizeWithAttributes method
CGSize size = [string sizeWithAttributes:#{NSFontAttributeName: [UIFont systemFontOfSize:17.0f]}];
// Values are fractional -- you should take the ceilf to get equivalent values
CGSize adjustedSize = CGSizeMake(ceilf(size.width), ceilf(size.height));

MultiLine UIlabel doesn't show all text?

I want to show label size as per text in it for that I have used
uilabel number of line property.
lbl.numberoflines = 0;
but it shows only three line and after that it shows ... . not all text showing.
when I will give number of line more than 3 then it showing that line in label.
appreciate for help
try this
lbl.lineBreakMode = UILineBreakModeWordWrap;
lbl.numberOfLines = 0;
if you want to manually calculate the height
lbl.numberOfLines = 0; // allows label to have as many lines as needed
lbl.text =#"xxxxxxxxxxxxxxxxxxxxxx";
CGSize labelSize = [ lbl.text sizeWithFont: lbl.font constrainedToSize:CGSizeMake(300, 300) lineBreakMode:NSLineBreakByWordWrapping];
// set the frame of labels here
else you can directly placed in Attribute
swift3
lbl.lineBreakMode = .byWordWrapping
lbl.numberOfLines = 0
// allows label to have as many lines as needed
lbl.text = "xxxxxxxxxxxxxxxxxxxxxx"
var labelSize = lbl.text.size(with: lbl.font, constrainedToSize: CGSize(width: 300, height: 300), lineBreakMode: .byWordWrapping)
// set the frame of labels here }
So I had made numberOfLines as 0 and had given lineBreakMode as WordWrap and had given proper constraints too.
What worked was changing the preferred width setting to automatic from explicit. i.e. from this:-
to this:-
try this
lbl.lineBreakMode = UILineBreakModeWordWrap;
lbl.numberOfLines = 0;
[lbl sizeTofit];
Use this method. May Help
-(CGSize)getLabelSize:(NSString *)text
{
UIFont *cellFont = [UIFont fontWithName:#"Helvetica" size:14.0];
CGSize constraintSize = CGSizeMake(300.0f, MAXFLOAT);
CGSize labelSize = [text sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
return labelSize;
}
All answers may helpful. but uses old approach.
can't you add Constraint to your view ?
It will be really easy to set layout constraint, and then you go.

How do I calculate the UILabel height dynamically [duplicate]

This question already has answers here:
How to calculate UILabel height dynamically?
(14 answers)
Closed 5 years ago.
I have the following code:
label.numberOfLines = 0; // allows label to have as many lines as needed
label.text = #"some long text";
[label sizeToFit];
How do I get the height of label in points?
The easiest way to get the height is sizeThatFits. Use it like this:
Objective-C
CGFloat maxLabelWidth = 100;
CGSize neededSize = [label sizeThatFits:CGSizeMake(maxLabelWidth, CGFLOAT_MAX)];
Swift 3.0
let maxLabelWidth: CGFloat = 100
let neededSize = label.sizeThatFits(CGSize(width: maxLabelWidth, height: CGFloat.greatestFiniteMagnitude))
The height your label needs is neededSize.height.
Note that im using CGFLOAT_MAX for the size height, to make sure the label has enough place to fit in the CGSize.
The height of your label also depends on your width of the label, thats why I added maxLabelWidth, it makes a difference if the label can be 100pt wide or 200pt.
Hope this helps!
Edit:
Make sure you set label.numberOfLines = 0; otherwise neededSize returns the size where the text is in a single line.
Edit:
Added Swift version, although the naming is a bit weird, greatestFiniteMagnitude seems to be the correct equivalent for CGFLOAT_MAX.
Use following method to calculate dynamic UILabel height:
- (CGFloat)getLabelHeight:(UILabel*)label
{
CGSize constraint = CGSizeMake(label.frame.size.width, CGFLOAT_MAX);
CGSize size;
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
CGSize boundingBox = [label.text boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:label.font}
context:context].size;
size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
return size.height;
}
Just use [label sizeThatFits:label.frame.size]; and it will return the size of label which will fit for given text. Or you can also follow the question
For those who want to estimate the size a label takes in a method which estimates header / cell height in UICollectionView or UITableView, follow this:
Set maxWidth that your label will take
Create a new UILabel and set numberOfLines to 0
Add Font attributes like custom font name and font size if using custom fonts
Set text for this label and get estimated height using sizeThatFits. Label height is neededHeight.height
Swift Version
let maxLabelWidth:CGFloat = collectionView.frame.width - 20
let label = UILabel()
label.numberOfLines = 0
let addressFont = [ NSFontAttributeName: UIFont(name: "OpenSans", size: 12.0)! ]
let addr = "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. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
label.attributedText = NSMutableAttributedString(string: addr , attributes: addressFont )
let neededSize:CGSize = label.sizeThatFits(CGSizeMake(maxLabelWidth, CGFloat.max))
let labelHeight = neededSize.height
Thanks to #FabioBerger
I edited Salman Zaidi's answer a little to make it work better for myself. It works well if you don't have direct access to a label, like when you are trying to get label height in heightForRowAtIndexPath:
-(CGFloat)getLabelHeight:(CGSize)labelSize string: (NSString *)string font: (UIFont *)font{
CGSize size;
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
CGSize boundingBox = [string boundingRectWithSize:labelSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:font}
context:context].size;
size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
return size.height;
}
You can Create Label dynamically :
-(CGRect)newLableSize:(NSString *)lableString
{
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setLineBreakMode:NSLineBreakByWordWrapping];
CGFloat tempwidth = YourStaticLabelWidth * ScreenWidth / 320;
NSMutableArray *array=[[NSMutableArray alloc]initWithObjects: lableString,nil];
CGRect newLabelsize = [[array objectAtIndex:0] boundingRectWithSize:CGSizeMake(tempwidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:selectFont,NSParagraphStyleAttributeName:paragraphStyle} context:nil];
NSLog(#"New Label Size Width : %f",newLabelsize.size.width);
NSLog(#"New Label Size Height : %f",newLabelsize.size.height);
return newLabelsize;
}
I'm adjust height, with 2 lines in my label
lblUserQuestion.preferredMaxLayoutWidth = 100.0f;
100.0f, it's a size i wanted, and another line,
[lblUserQuestion sizeToFit];
My method complete is,
UILabel *lblUserQuestion = [[UILabel alloc] initWithFrame:CGRectMake(61, 25, self.frame.size.width-61-20, 37.0f)];
lblUserQuestion.numberOfLines= 0;
lblUserQuestion.font =[UIFont fontWithName:#"HelveticaNeue-Thin" size:14.];
lblUserQuestion.adjustsFontSizeToFitWidth = YES;
lblUserQuestion.minimumScaleFactor = 0.5;
lblUserQuestion.preferredMaxLayoutWidth= 100.0f;
lblUserQuestion.text = _photoToVote.label;
- (CGFloat)getTextHeightByWidth:(NSString*)text textFont:(UIFont*)textFont textWidth:(float)textWidth {
if (!text) {
return 0;
}
CGSize boundingSize = CGSizeMake(textWidth, CGFLOAT_MAX);
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:#{ NSFontAttributeName: textFont }];
CGRect rect = [attributedText boundingRectWithSize:boundingSize options:NSStringDrawingUsesLineFragmentOrigin context:nil];
CGSize requiredSize = rect.size;
return requiredSize.height;
}
- (CGFloat)getTextWidthByHeight:(NSString*)text textFont:(UIFont*)textFont textHeight:(float)textHeight {
if (!text) {
return 0.0f;
}
CGSize boundingSize = CGSizeMake(CGFLOAT_MAX, textHeight);
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text
attributes:#{ NSFontAttributeName: textFont }];
CGRect rect = [attributedText boundingRectWithSize:boundingSize
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
CGSize requiredSize = rect.size;
return requiredSize.width;
}
Use this function
+ (CGFloat)heightForText:(NSString*)text font:(UIFont*)font withinWidth:(CGFloat)width {
CGSize constraint = CGSizeMake(width, 20000.0f);
CGSize size;
CGSize boundingBox = [text boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:font}
context:nil].size;
size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
return size.height;
}
- (CGFloat)getLabelsize:(UILabel *)label
{
CGSize maxSize = CGSizeMake(label.frame.size.width, 9999);
CGSize requiredSize = [label sizeThatFits:maxSize];
return requiredSize.height;
}

iOS : How can I set frame of UILabel according to its text?

I want to develop a functionality that to set frame of UILabel according to its text means when text is change its frame is change? If yes, then please share any link or any idea to develop this.
Thanks in advance.
label.text = #"some text of random length";
[label sizeToFit];
If the text might be more than 1 line, add label.numberOfLines = 0; before calling sizeToFit;
try this
NSString *sample = #"...";
CGSize txtSz = [sample sizeWithFont:[UIFont fontWithName: #"Helvetica" size: 16]];
CGRect lblFrame = CGRectMake(10,50, txtSz.width, txtSz.height);
yourLabel.frame = lblFrame;
It's 2016 and sizeWithFont is long gone.
Now, and in Swift 3, use:
let text = "Random Text"
let size = (text as NSString).size(attributes: [NSFontAttributeName : UIFont.systemFont(ofSize: 50)])
let label = UILabel(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height))
If you want to know needful height for some width you can use the code below:
NSString *yourString = #"My great text o0";
CGSize s = [yourString sizeWithFont:[UIFont systemFontOfSize:12] constrainedToSize:CGSizeMake(width, MAXFLOAT) lineBreakMode:UILineBreakModeWordWrap];
// s.height is your your height
For more information read here

Resources