ChatGPT解决这个技术问题 Extra ChatGPT

Adjust UILabel height depending on the text

Consider I have the following text in a UILabel (a long line of dynamic text):

Since the alien army vastly outnumbers the team, players must use the post-apocalyptic world to their advantage, such as seeking cover behind dumpsters, pillars, cars, rubble, and other objects.

I want to resize the UILabel's height so that the text can fit in. I'm using following properties of UILabel to make the text within to wrap.

myUILabel.lineBreakMode = UILineBreakModeWordWrap;
myUILabel.numberOfLines = 0;

Please let me know if I'm not heading in the right direction. Thanks.


A
Adam Eberbach

sizeWithFont constrainedToSize:lineBreakMode: is the method to use. An example of how to use it is below:

//Calculate the expected size based on the font and linebreak mode of your label
// FLT_MAX here simply means no constraint in height
CGSize maximumLabelSize = CGSizeMake(296, FLT_MAX);

CGSize expectedLabelSize = [yourString sizeWithFont:yourLabel.font constrainedToSize:maximumLabelSize lineBreakMode:yourLabel.lineBreakMode];   

//adjust the label the the new height.
CGRect newFrame = yourLabel.frame;
newFrame.size.height = expectedLabelSize.height;
yourLabel.frame = newFrame;

This uses 9999, how would you do it flexible to the text?
@quantumpotato 9999 is just a place holder for the maximum space the text is allowed to take up. You can use any number there that works for your UI.
Is there anyway to use a line break with this method?
If you are sizing your labels like this, you are doing it wrong. You should use [label sizeToFit].
Don't forget that sizeWithFont is deprecated in iOS 7. stackoverflow.com/questions/18897896/…
D
DonnaLea

You were going in the right direction. All you need to do is:

myUILabel.numberOfLines = 0;
myUILabel.text = @"Enter large amount of text here";
[myUILabel sizeToFit];

The size to fit was exactly what i needed to get the text to wrap, long with myUILabel.lineBreakMode = UILineBreakModeWordWrap; myUILabel.numberOfLines = 0;
A much easier solution than the answer marked as correct and works just as well.
@Inder Kumar Rathore - I use this for multiple lines, all the time, hence the numberOfLines = 0; I guess it's missing setting the preffered width first, but I assumed that had already been done with the init of the UILabel.
@Donna.. I din't get your preferred with.. are you talking about its frame??
@DonnaLea, Thank you very much. your simple approach to the solution helped me to solve my problem too.
C
Community

In iOS 6 Apple has added a property to UILabel that greatly simplifies dynamic vertical resizing of labels: preferredMaxLayoutWidth.

Using this property in combination with lineBreakMode = NSLineBreakByWordWrapping and sizeToFit method allows easily resize a UILabel instance to the height that accommodates the entire text.

A quote from iOS documentation:

preferredMaxLayoutWidth The preferred maximum width (in points) for a multiline label. Discussion This property affects the size of the label when layout constraints are applied to it. During layout, if the text extends beyond the width specified by this property, the additional text is flowed to one or more new lines, thereby increasing the height of the label.

A sample:

...
UILabel *status = [[UILabel alloc] init];
status.lineBreakMode = NSLineBreakByWordWrapping;
status.numberOfLines = 5; // limits to 5 lines; use 0 for unlimited.

[self addSubview:status]; // self here is the parent view

status.preferredMaxLayoutWidth = self.frame.size.width; // assumes the parent view has its frame already set.

status.text = @"Some quite lengthy message may go here…";
[status sizeToFit];
[status setNeedsDisplay];
...

B
Badal Shah

Check this work perfectly without adding Single line of code. (Using Autolayout)

I made a demo for you according to your requirement. Download it from below link,

Autoresize UIView and UILabel

Step by Step Guide :-

Step 1 :- Set constrain to UIView

1) Leading 2) Top 3) Trailing (From mainview)

https://i.stack.imgur.com/M6Ck5.gif

Step 2 :- Set constrain to Label 1

1) Leading 2) Top 3) Trailing (From it's superview)

https://i.stack.imgur.com/FeBFW.gif

Step 3 :- Set constrain to Label 2

1) Leading 2) Trailing (From it's superview)

https://i.stack.imgur.com/GLCVp.gif

Step 4 :- Most tricky give botton to UILabel from UIView .

https://i.stack.imgur.com/WcjmM.gif

Step 5 :- (Optional) Set constrain to UIButton

1) Leading 2) Bottom 3) Trailing 4) Fixed Height (From mainview)

https://i.stack.imgur.com/CYwZG.gif

Output :-

https://i.stack.imgur.com/zaM0A.gif

Note :- Make sure you have set Number of lines =0 in Label property.

https://i.stack.imgur.com/g3w1L.png

I hope this info enough to understand Autoresize UIView according to UILabel's height and Autoresize UILabel According to text.


And what if you would want to also have some views under the white-background view? Not giving it a height will show some red lines in SB for "Need constrains for: Y position or height"
this needs to be marked as the correct answer. This is the right way with ib.
@SPQR3 whats the problem ? it helped to lot of people and its working fine.
What exactly I'm looking for. Great job.
m
meaning-matters

Instead doing this programmatically, you can do this in Storyboard/XIB while designing.

Set UIlabel's number of lines property to 0 in attribute inspector.

Then set width constraint/(or) leading and trailing constraint as per the requirement.

Then set height constraint with minimum value. Finally select the height constraint you added and in the size inspector the one next to attribute inspector, change the height constraint's relation from equal to - greater than.


This one works for UILabel embedded in a custom xib cell in a UITableView.
According to your answer i set a constant height to my label and set that priority to Low (250) and errors disappear . (Don't need to set equal to - greater than)
C
Chase Roberts

Thanks guys for help, here is the code I tried which is working for me

   UILabel *instructions = [[UILabel alloc]initWithFrame:CGRectMake(10, 225, 300, 180)];
   NSString *text = @"First take clear picture and then try to zoom in to fit the ";
   instructions.text = text;
   instructions.textAlignment = UITextAlignmentCenter;
   instructions.lineBreakMode = NSLineBreakByWordWrapping;
   [instructions setTextColor:[UIColor grayColor]];

   CGSize expectedLabelSize = [text sizeWithFont:instructions.font 
                                constrainedToSize:instructions.frame.size
                                    lineBreakMode:UILineBreakModeWordWrap];

    CGRect newFrame = instructions.frame;
    newFrame.size.height = expectedLabelSize.height;
    instructions.frame = newFrame;
    instructions.numberOfLines = 0;
    [instructions sizeToFit];
    [self addSubview:instructions];

sizeWithFont is deprecated.
V
Vijay-Apple-Dev.blogspot.com

Solution to iOS7 prior and iOS7 above

//
//  UILabel+DynamicHeight.m
//  For StackOverFlow
//
//  Created by Vijay on 24/02/14.
//  Copyright (c) 2014 http://Vijay-Apple-Dev.blogspot.com. All rights reserved.
//

#import <UIKit/UIKit.h>

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)

#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)

#define iOS7_0 @"7.0"

@interface UILabel (DynamicHeight)

/*====================================================================*/

/* Calculate the size,bounds,frame of the Multi line Label */

/*====================================================================*/
/**
 *  Returns the size of the Label
 *
 *  @param aLabel To be used to calculte the height
 *
 *  @return size of the Label
 */

-(CGSize)sizeOfMultiLineLabel;

@end


//
//  UILabel+DynamicHeight.m
//  For StackOverFlow
//
//  Created by Vijay on 24/02/14.
//  Copyright (c) 2014 http://Vijay-Apple-Dev.blogspot.com. All rights reserved.
//

#import "UILabel+DynamicHeight.h"

@implementation UILabel (DynamicHeight)
/*====================================================================*/

/* Calculate the size,bounds,frame of the Multi line Label */

/*====================================================================*/
/**
 *  Returns the size of the Label
 *
 *  @param aLabel To be used to calculte the height
 *
 *  @return size of the Label
 */
-(CGSize)sizeOfMultiLineLabel{

    NSAssert(self, @"UILabel was nil");

    //Label text
    NSString *aLabelTextString = [self text];

    //Label font
    UIFont *aLabelFont = [self font];

    //Width of the Label
    CGFloat aLabelSizeWidth = self.frame.size.width;


    if (SYSTEM_VERSION_LESS_THAN(iOS7_0)) {
        //version < 7.0

        return [aLabelTextString sizeWithFont:aLabelFont
                            constrainedToSize:CGSizeMake(aLabelSizeWidth, MAXFLOAT)
                                lineBreakMode:NSLineBreakByWordWrapping];
    }
    else if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(iOS7_0)) {
        //version >= 7.0

        //Return the calculated size of the Label
        return [aLabelTextString boundingRectWithSize:CGSizeMake(aLabelSizeWidth, MAXFLOAT)
                                              options:NSStringDrawingUsesLineFragmentOrigin
                                           attributes:@{
                                                        NSFontAttributeName : aLabelFont
                                                        }
                                              context:nil].size;

    }

    return [self bounds].size;

}

@end

In UITableViewController's subclass, where should I call this method?
Where you want to calculate the label height then call this method. then adjust the height of the table view. where you have tableview height for row at index method. calculate all vertical labels text as u needed
Vijay sizeWithFont is depricated.
p
pkarc

Since sizeWithFont is deprecated I use this one instead.

this one get label specific attributes.

-(CGFloat)heightForLabel:(UILabel *)label withText:(NSString *)text{

    NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName:label.font}];
    CGRect rect = [attributedText boundingRectWithSize:(CGSize){label.frame.size.width, CGFLOAT_MAX}
                                           options:NSStringDrawingUsesLineFragmentOrigin
                                           context:nil];

    return ceil(rect.size.height);
}

P
Peter Kreinz

UILabel extension based on this answer for Swift 4 and above

extension UILabel {

    func retrieveTextHeight () -> CGFloat {
        let attributedText = NSAttributedString(string: self.text!, attributes: [NSFontAttributeName:self.font])

        let rect = attributedText.boundingRect(with: CGSize(width: self.frame.size.width, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil)

        return ceil(rect.size.height)
    }

}

Can be used like:

self.labelHeightConstraint.constant = self.label.retrieveTextHeight()

b
bbrame

Here is a category version:

UILabel+AutoSize.h #import

@interface UILabel (AutoSize)

- (void) autosizeForWidth: (int) width;

@end

UILabel+AutoSize.m

#import "UILabel+AutoSize.h"

@implementation UILabel (AutoSize)

- (void) autosizeForWidth: (int) width {
    self.lineBreakMode = UILineBreakModeWordWrap;
    self.numberOfLines = 0;
    CGSize maximumLabelSize = CGSizeMake(width, FLT_MAX);
    CGSize expectedLabelSize = [self.text sizeWithFont:self.font constrainedToSize:maximumLabelSize lineBreakMode:self.lineBreakMode];
    CGRect newFrame = self.frame;
    newFrame.size.height = expectedLabelSize.height;
    self.frame = newFrame;
}

@end

k
klefevre

You can implement TableViewController's (UITableViewCell *)tableView:cellForRowAtIndexPath method in the following way (for example) :

#define CELL_LABEL_TAG 1

- (UITableViewCell *)tableView:(UITableView *)tableView  cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *text = @"my long text";

    static NSString *MyIdentifier = @"MyIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero  reuseIdentifier:identifier] autorelease];
    }

    CGFloat width = [UIScreen mainScreen].bounds.size.width - 50;
    CGFloat height = [self textHeight:text] + 10;
    CGRect frame = CGRectMake(10.0f, 10.0f, width, height);

    UILabel *cellLabel = [[UILabel alloc] initWithFrame:frame];
    cellLabel.tag = CELL_LABEL_TAG;
    cellLabel.textColor = [UIColor blackColor];
    cellLabel.backgroundColor = [UIColor clearColor];
    cellLabel.textAlignment = UITextAlignmentLeft;
    cellLabel.font = [UIFont systemFontOfSize:12.0f];
    [cell.contentView addSubview:cellLabel];
    [cellLabel release];

    return cell;
}

UILabel *label = (UILabel *)[cell viewWithTag:CELL_LABEL_TAG];
label.text = text;
label.numberOfLines = 0;
[label sizeToFit];
return cell;

Also use NSString's sizeWithFont:constrainedToSize:lineBreakMode: method to compute the text's height.


A
AechoLiu

My approach to compute the dynamic height of UILabel.

    let width = ... //< width of this label 
    let text = ... //< display content

    label.numberOfLines = 0
    label.lineBreakMode = .byWordWrapping
    label.preferredMaxLayoutWidth = width

    // Font of this label.
    //label.font = UIFont.systemFont(ofSize: 17.0)
    // Compute intrinsicContentSize based on font, and preferredMaxLayoutWidth
    label.invalidateIntrinsicContentSize() 
    // Destination height
    let height = label.intrinsicContentSize.height

Wrap to function:

func computeHeight(text: String, width: CGFloat) -> CGFloat {
    // A dummy label in order to compute dynamic height.
    let label = UILabel()

    label.numberOfLines = 0
    label.lineBreakMode = .byWordWrapping
    label.font = UIFont.systemFont(ofSize: 17.0)

    label.preferredMaxLayoutWidth = width
    label.text = text
    label.invalidateIntrinsicContentSize()

    let height = label.intrinsicContentSize.height
    return height
}

P
Paul Ardeleanu

And for those that are migrating to iOS 8, here is a class extension for Swift:

extension UILabel {

    func autoresize() {
        if let textNSString: NSString = self.text {
            let rect = textNSString.boundingRectWithSize(CGSizeMake(self.frame.size.width, CGFloat.max),
                options: NSStringDrawingOptions.UsesLineFragmentOrigin,
                attributes: [NSFontAttributeName: self.font],
                context: nil)
            self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, rect.height)
        }
    }

}

B
Benjamin W.

The easiest and better way that worked for me was to apply height constraint to label and set the priority to low, i.e., (250) in storyboard.

So you need not worry about calculating the height and width programmatically, thanks to storyboard.


S
Sandeep Singh

Updated Method

+ (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;
}

S
Sagar Sukode

This is one line of code to get the UILabel Height using Objective-c:

labelObj.numberOfLines = 0;
CGSize neededSize = [labelObj sizeThatFits:CGSizeMake(screenWidth, CGFLOAT_MAX)];

and using .height you will get the height of label as follows:

neededSize.height

Best current answer.
R
Rohit Makwana

You can get height using below code

You have to pass

text 2. font 3. label width func heightForLabel(text: String, font: UIFont, width: CGFloat) -> CGFloat { let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude)) label.numberOfLines = 0 label.lineBreakMode = NSLineBreakMode.byWordWrapping label.font = font label.text = text label.sizeToFit() return label.frame.height }


T
Tim Stephenson

Thanks for this post. It helped me a great deal. In my case I am also editing the text in a separate view controller. I noticed that when I use:

[cell.contentView addSubview:cellLabel];

in the tableView:cellForRowAtIndexPath: method that the label view was continually rendered over the top of the previous view each time I edited the cell. The text became pixelated, and when something was deleted or changed, the previous version was visible under the new version. Here's how I solved the problem:

if ([[cell.contentView subviews] count] > 0) {
    UIView *test = [[cell.contentView subviews] objectAtIndex:0];
    [test removeFromSuperview];
}
[cell.contentView insertSubview:cellLabel atIndex:0];

No more weird layering. If there is a better way to handle this, Please let me know.


a
ashokdy
UILabel *itemTitle = [[UILabel alloc] initWithFrame:CGRectMake(10.0f, 10,100, 200.0f)];
itemTitle.text = @"aseruy56uiytitfesh";
itemTitle.adjustsFontSizeToFitWidth = NO;
itemTitle.autoresizingMask = UIViewAutoresizingFlexibleWidth;
itemTitle.font = [UIFont boldSystemFontOfSize:18.0];
itemTitle.textColor = [UIColor blackColor];
itemTitle.shadowColor = [UIColor whiteColor];
itemTitle.shadowOffset = CGSizeMake(0, 1);
itemTitle.backgroundColor = [UIColor blueColor];
itemTitle.lineBreakMode = UILineBreakModeWordWrap;
itemTitle.numberOfLines = 0;
[itemTitle sizeToFit];
[self.view addSubview:itemTitle];

use this here all the properties are used on the label and test it by increasing the text in the itemTitle.text as

itemTitle.text = @"diofgorigjveghnhkvjteinughntivugenvitugnvkejrfgnvkhv";

it will show the perfetc answer as you need


M
MSU_Bulldog

You may use it as a method, as well. @Pyjamasam is very much true so i am just making its method. It may be helpfull for some one else

-(CGRect)setDynamicHeightForLabel:(UILabel*)_lbl andMaxWidth:(float)_width{
    CGSize maximumLabelSize = CGSizeMake(_width, FLT_MAX);

    CGSize expectedLabelSize = [_lbl.text sizeWithFont:_lbl.font constrainedToSize:maximumLabelSize lineBreakMode:_lbl.lineBreakMode];

    //adjust the label the the new height.
    CGRect newFrame = _lbl.frame;
    newFrame.size.height = expectedLabelSize.height;
    return newFrame;
}

and just set it like this

label.frame = [self setDynamicHeightForLabel:label andMaxWidth:300.0];

B
Baxter

To do this in Swift3 following is the code:

 let labelSizeWithFixedWith = CGSize(width: 300, height: CGFloat.greatestFiniteMagnitude)
            let exactLabelsize = self.label.sizeThatFits(labelSizeWithFixedWith)
            self.label.frame = CGRect(origin: CGPoint(x: 20, y: 20), size: exactLabelsize)

C
Chetan A

Adding to the above answers:

This can be easily achieved via storyboard.

Set constraint for UILabel.(In my case I did top, left and fixed width) Set Number of line to 0 in Attribute Inspector Set Line Break to WordWrap in Attribute Inspector.

https://i.stack.imgur.com/NZkmJ.png


D
David Weiss

One line is Chris's answer is wrong.

newFrame.size.height = maximumLabelSize.height;

should be

newFrame.size.height = expectedLabelSize.height;

Other than that, it's the correct solution.


p
pablasso

Finally, it worked. Thank you guys.

I was not getting it to work because i was trying to resize the label in heightForRowAtIndexPath method:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

and (yeah silly me), i was resizing the label to default in cellForRowAtIndexPath method - i was overlooking the code i had written earlier:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

F
Frank Fu
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    cellIdentifier = @"myCell";
    cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    cell.myUILabel.lineBreakMode = UILineBreakModeWordWrap;        
    cell.myUILabel.numberOfLines = 0;
    cell.myUILabel.text = @"Some very very very very long text....."
    [cell.myUILabel.criterionDescriptionLabel sizeToFit];    
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
    CGFloat rowHeight = cell.myUILabel.frame.size.height + 10;

    return rowHeight;    
}

Can you add some explanation to how your solution solves the OP's issue?
you cannot ask UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath]; in heightForRowAtIndexPath, you will ge t and infinite loop
G
Gaurav Gilani
NSString *str = @"Please enter your text......";
CGSize lblSize = [str sizeWithFont:[UIFont systemFontOfSize:15] constrainedToSize: CGSizeMake(200.0f, 600.0f) lineBreakMode: NSLineBreakByWordWrapping];

UILabel *label = [[UILabel alloc]init];
label.frame = CGRectMake(60, 20, 200, lblSize.height);
label.numberOfLines = 0;
label.lineBreakMode = NSLineBreakByWordWrapping;
label.font = [UIFont systemFontOfSize:15];
label.text = str;
label.backgroundColor = [UIColor clearColor];
[label sizeToFit];
[self.view addSubview:label];

P
PhuocLuong

My code:

UILabel *label      = [[UILabel alloc] init];
label.numberOfLines = 0;
label.lineBreakMode = NSLineBreakByWordWrapping;
label.text          = text;
label.textAlignment = NSTextAlignmentCenter;
label.font          = [UIFont fontWithName:_bodyTextFontFamily size:_bodyFontSize];

CGSize size = [label sizeThatFits:CGSizeMake(width, MAXFLOAT)];


float height        = size.height;
label.frame         = CGRectMake(x, y, width, height);

P
Prashant Tukadiya

This method will give perfect height

-(float) getHeightForText:(NSString*) text withFont:(UIFont*) font andWidth:(float) width{
CGSize constraint = CGSizeMake(width , 20000.0f);
CGSize title_size;
float totalHeight;


title_size = [text boundingRectWithSize:constraint
                                options:NSStringDrawingUsesLineFragmentOrigin
                             attributes:@{ NSFontAttributeName : font }
                                context:nil].size;

totalHeight = ceil(title_size.height);

CGFloat height = MAX(totalHeight, 40.0f);
return height;
}

Do not copy content from elsewhere without clear attribution. It is seen as plagiarism. See stackoverflow.com/help/referencing (stackoverflow.com/a/25158206/444991).
J
Juan Boero

Swift 2:

    yourLabel.text = "your very long text"
    yourLabel.numberOfLines = 0
    yourLabel.lineBreakMode = NSLineBreakMode.ByWordWrapping
    yourLabel.frame.size.width = 200
    yourLabel.frame.size.height = CGFloat(MAXFLOAT)
    yourLabel.sizeToFit()

The interesting lines are sizeToFit() in conjunction with setting a frame.size.height to the max float, this will give room for long text, but sizeToFit() will force it to only use the necessary, but ALWAYS call it after setting the .frame.size.height .

I recommend setting a .backgroundColor for debug purposes, this way you can see the frame being rendered for each case.


yourLabel.frame.height is a get only property
yourLabel.frame.size.width and yourLabel.frame.size.height are read only as well
g
garg
myLabel.text = "your very long text"
myLabel.numberOfLines = 0
myLabel.lineBreakMode = NSLineBreakMode.ByWordWrapping

Please set constraints for UILable in storyboard including top left bottom right