Laggy when switching tabBarController to a tableView with many cells - ios

My app is based on a tabBarController and has 2 tabs.
I load data for the 2nd tab in the background while the app launches at tab 1, so users don't need to wait when they click tab 2.
My problem is: it's laggy to switch to tab 2 if I load the data in advance and there is much data to show in cells of the tableView belonging to tab 2. If there are not many cells to show, then it's not laggy at all.
I guess it's because generating cells is time-consuming, so the view is blocked when there are too many cells. How do I optimize this?

Important! This code has not been tested in the XCode, but contains some parts from real projects.
The CellView class could be created either as a Nib or even manually using calculateCellHeight method from ServicesHelper.m. In both cases the layoutSubviews method must be implemented where the resize detailTextLabel UILabel code to be placed. The CellView labels Font and Color must be the same as used in calculateCellHeight method.
ServicesHelper.m
#define FontRegular(fontSize) [UIFont fontWithName:#"HelveticaNeue" size:fontSize]
// the height of fixed part of the cell, fixed height UILabel + some padding
#define kFixedPartCellHeight 20
#define kLeftPaddingWidth 20
#define kLandscapeHeightKey #"landscapeKey"
#define kPortraitHeight #"portraitKey"
+ (NSDictionary *) calculateCellHeight: (NSString *) text {
// dynamic height label
UILabel *detailTextLabel = [UILabel new];
UIFont *mainFont = FontRegular(14.0);
// specifying font and colour to be used inside cell is important to get precise frame rect
[detailTextLabel setFont: mainFont];
[detailTextLabel setTextColor:[UIColor blackColor]];
detailTextLabel.lineBreakMode = NSLineBreakByWordWrapping;
detailTextLabel.numberOfLines = 0;
[textLabel setBackgroundColor: [UIColor clearColor]];
[detailTextLabel setBackgroundColor: [UIColor clearColor]];
// get the width of the cell for both orientations
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat landscapeWidth = MAX (screenRect.size.width, screenRect.size.height);
CGFloat portraitWidth = MIN (screenRect.size.width, screenRect.size.height);
// kLeftPaddingWidth - is just a white space left and right to the UILabel inside cell
// we set the UILabel width with maximum possible height, then set text and shrink it using sizeToFit to get the exact size
detailTextLabel.frame = CGRectMake (0, 0 , landscapeWidth - kLeftPaddingWidth * 2, CGFLOAT_MAX);
textLabel.frame = detailTextLabel.frame;
detailTextLabel.text = text;
[detailTextLabel sizeToFit];
CGFloat landscapeHeight = detailTextLabel.frame.size.height + kFixedPartCellHeight;
detailTextLabel.frame = CGRectMake (0, 0 , portraitWidth - kLeftPaddingWidth * 2, CGFLOAT_MAX);
textLabel.frame = detailTextLabel.frame;
detailTextLabel.text = text;
[detailTextLabel sizeToFit];
CGFloat portraitHeight = detailTextLabel.frame.size.height + kFixedPartCellHeight;
return #{kLandscapeHeightKey: landscapeHeight, kPortraitHeightKey: portraitHeight};
}
TableView.h
#property (nonatomic, strong) NSMutableArray *arrayOfTexts;
#property (nonatomic, strong) NSMutableDictionary *dictOfHeights;
TableView.m
- (void) precalculateHeight {
if (nil == self.dictOfHeights) self.dictOfHeights = [NSMutableDictionary new];
CGRect screenRect = [[UIScreen mainScreen] bounds];
// height of two screens
CGFloat maxHeight = MAX (screenRect.size.width, screenRect.size.height) * 2;
CGFloat totalHeight = 0;
CGFloat portraitHeight;
CGFloat landscapeHeight;
NSDictionary *dictHeights;
for (int i = 0; i < self.arrayOfTexts.count; i++ ) {
dictHeights = [ServicesHelper calculateCellHeight: arrayOfTexts[i]];
portraitHeight = [dictHeights[kPortraitHeightKey] floatValue];
[self.dictOfHeights setValue: dictHeights forKey:#(i)];
totalHeight += portraitHeight;
if (totalHeight > maxHeight) break;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.dictOfHeights && self.dictOfHeights[#(indexPath.row)]) {
return UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation) ?
[self.dictOfHeights[#(indexPath.row)][kPortraitHeightKey] floatValue] :
[self.dictOfHeights[#(indexPath.row)][kLandscapeHeightKey] floatValue];
} else {
// you decide, call calculateCellHeight for particular row, right from here, or also calculate rows height for the coming set of cells
// #todo:
}
return CGFLOAT_MIN;
}

Related

Fit background color to special characters [duplicate]

I have a UILabel with space for two lines of text. Sometimes, when the text is too short, this text is displayed in the vertical center of the label.
How do I vertically align the text to always be at the top of the UILabel?
There's no way to set the vertical-align on a UILabel, but you can get the same effect by changing the label's frame. I've made my labels orange so you can see clearly what's happening.
Here's the quick and easy way to do this:
[myLabel sizeToFit];
If you have a label with longer text that will make more than one line, set numberOfLines to 0 (zero here means an unlimited number of lines).
myLabel.numberOfLines = 0;
[myLabel sizeToFit];
Longer Version
I'll make my label in code so that you can see what's going on. You can set up most of this in Interface Builder too. My setup is a View-Based App with a background image I made in Photoshop to show margins (20 points). The label is an attractive orange color so you can see what's going on with the dimensions.
- (void)viewDidLoad
{
[super viewDidLoad];
// 20 point top and left margin. Sized to leave 20 pt at right.
CGRect labelFrame = CGRectMake(20, 20, 280, 150);
UILabel *myLabel = [[UILabel alloc] initWithFrame:labelFrame];
[myLabel setBackgroundColor:[UIColor orangeColor]];
NSString *labelText = #"I am the very model of a modern Major-General, I've information vegetable, animal, and mineral";
[myLabel setText:labelText];
// Tell the label to use an unlimited number of lines
[myLabel setNumberOfLines:0];
[myLabel sizeToFit];
[self.view addSubview:myLabel];
}
Some limitations of using sizeToFit come into play with center- or right-aligned text. Here's what happens:
// myLabel.textAlignment = NSTextAlignmentRight;
myLabel.textAlignment = NSTextAlignmentCenter;
[myLabel setNumberOfLines:0];
[myLabel sizeToFit];
The label is still sized with a fixed top-left corner. You can save the original label's width in a variable and set it after sizeToFit, or give it a fixed width to counter these problems:
myLabel.textAlignment = NSTextAlignmentCenter;
[myLabel setNumberOfLines:0];
[myLabel sizeToFit];
CGRect myFrame = myLabel.frame;
// Resize the frame's width to 280 (320 - margins)
// width could also be myOriginalLabelFrame.size.width
myFrame = CGRectMake(myFrame.origin.x, myFrame.origin.y, 280, myFrame.size.height);
myLabel.frame = myFrame;
Note that sizeToFit will respect your initial label's minimum width. If you start with a label 100 wide and call sizeToFit on it, it will give you back a (possibly very tall) label with 100 (or a little less) width. You might want to set your label to the minimum width you want before resizing.
Some other things to note:
Whether lineBreakMode is respected depends on how it's set. NSLineBreakByTruncatingTail (the default) is ignored after sizeToFit, as are the other two truncation modes (head and middle). NSLineBreakByClipping is also ignored. NSLineBreakByCharWrapping works as usual. The frame width is still narrowed to fit to the rightmost letter.
Mark Amery gave a fix for NIBs and Storyboards using Auto Layout in the comments:
If your label is included in a nib or storyboard as a subview of the view of a ViewController that uses autolayout, then putting your sizeToFit call into viewDidLoad won't work, because autolayout sizes and positions the subviews after viewDidLoad is called and will immediately undo the effects of your sizeToFit call. However, calling sizeToFit from within viewDidLayoutSubviews will work.
My Original Answer (for posterity/reference):
This uses the NSString method sizeWithFont:constrainedToSize:lineBreakMode: to calculate the frame height needed to fit a string, then sets the origin and width.
Resize the frame for the label using the text you want to insert. That way you can accommodate any number of lines.
CGSize maximumSize = CGSizeMake(300, 9999);
NSString *dateString = #"The date today is January 1st, 1999";
UIFont *dateFont = [UIFont fontWithName:#"Helvetica" size:14];
CGSize dateStringSize = [dateString sizeWithFont:dateFont
constrainedToSize:maximumSize
lineBreakMode:self.dateLabel.lineBreakMode];
CGRect dateFrame = CGRectMake(10, 10, 300, dateStringSize.height);
self.dateLabel.frame = dateFrame;
Set the new text:
myLabel.text = #"Some Text"
Set the maximum number of lines to 0 (automatic):
myLabel.numberOfLines = 0
Set the frame of the label to the maximum size:
myLabel.frame = CGRectMake(20,20,200,800)
Call sizeToFit to reduce the frame size so the contents just fit:
[myLabel sizeToFit]
The labels frame is now just high and wide enough to fit your text. The top left should be unchanged. I have tested this only with the top left-aligned text. For other alignments, you might have to modify the frame afterward.
Also, my label has word wrapping enabled.
Refering to the extension solution:
for(int i=1; i< newLinesToPad; i++)
self.text = [self.text stringByAppendingString:#"\n"];
should be replaced by
for(int i=0; i<newLinesToPad; i++)
self.text = [self.text stringByAppendingString:#"\n "];
Additional space is needed in every added newline, because iPhone UILabels' trailing carriage returns seems to be ignored :(
Similarly, alignBottom should be updated too with a #" \n#%" in place of "\n#%" (for cycle initialization must be replaced by "for(int i=0..." too).
The following extension works for me:
// -- file: UILabel+VerticalAlign.h
#pragma mark VerticalAlign
#interface UILabel (VerticalAlign)
- (void)alignTop;
- (void)alignBottom;
#end
// -- file: UILabel+VerticalAlign.m
#implementation UILabel (VerticalAlign)
- (void)alignTop {
CGSize fontSize = [self.text sizeWithFont:self.font];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i<newLinesToPad; i++)
self.text = [self.text stringByAppendingString:#"\n "];
}
- (void)alignBottom {
CGSize fontSize = [self.text sizeWithFont:self.font];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i<newLinesToPad; i++)
self.text = [NSString stringWithFormat:#" \n%#",self.text];
}
#end
Then call [yourLabel alignTop]; or [yourLabel alignBottom]; after each yourLabel text assignment.
Just in case it's of any help to anyone, I had the same problem but was able to solve the issue simply by switching from using UILabel to using UITextView. I appreciate this isn't for everyone because the functionality is a bit different.
If you do switch to using UITextView, you can turn off all the Scroll View properties as well as User Interaction Enabled... This will force it to act more like a label.
No muss, no fuss
#interface MFTopAlignedLabel : UILabel
#end
#implementation MFTopAlignedLabel
- (void)drawTextInRect:(CGRect) rect
{
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:self.text attributes:#{NSFontAttributeName:self.font}];
rect.size.height = [attributedText boundingRectWithSize:rect.size
options:NSStringDrawingUsesLineFragmentOrigin
context:nil].size.height;
if (self.numberOfLines != 0) {
rect.size.height = MIN(rect.size.height, self.numberOfLines * self.font.lineHeight);
}
[super drawTextInRect:rect];
}
#end
No muss, no Objective-c, no fuss but Swift 3:
class VerticalTopAlignLabel: UILabel {
override func drawText(in rect:CGRect) {
guard let labelText = text else { return super.drawText(in: rect) }
let attributedText = NSAttributedString(string: labelText, attributes: [NSFontAttributeName: font])
var newRect = rect
newRect.size.height = attributedText.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil).size.height
if numberOfLines != 0 {
newRect.size.height = min(newRect.size.height, CGFloat(numberOfLines) * font.lineHeight)
}
super.drawText(in: newRect)
}
}
Swift 4.2
class VerticalTopAlignLabel: UILabel {
override func drawText(in rect:CGRect) {
guard let labelText = text else { return super.drawText(in: rect) }
let attributedText = NSAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font])
var newRect = rect
newRect.size.height = attributedText.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil).size.height
if numberOfLines != 0 {
newRect.size.height = min(newRect.size.height, CGFloat(numberOfLines) * font.lineHeight)
}
super.drawText(in: newRect)
}
}
Easiest approach using Storyboard:
Embed Label in a StackView and set the following two attributes of StackView in the Attribute Inspector:
1- Axis to Horizontal,
2- Alignment to Top
Like the answer above, but it wasn't quite right, or easy to slap into code so I cleaned it up a bit. Add this extension either to it's own .h and .m file or just paste right above the implementation you intend to use it:
#pragma mark VerticalAlign
#interface UILabel (VerticalAlign)
- (void)alignTop;
- (void)alignBottom;
#end
#implementation UILabel (VerticalAlign)
- (void)alignTop
{
CGSize fontSize = [self.text sizeWithFont:self.font];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i<= newLinesToPad; i++)
{
self.text = [self.text stringByAppendingString:#" \n"];
}
}
- (void)alignBottom
{
CGSize fontSize = [self.text sizeWithFont:self.font];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i< newLinesToPad; i++)
{
self.text = [NSString stringWithFormat:#" \n%#",self.text];
}
}
#end
And then to use, put your text into the label, and then call the appropriate method to align it:
[myLabel alignTop];
or
[myLabel alignBottom];
An even quicker (and dirtier) way to accomplish this is by setting the UILabel's line break mode to "Clip" and adding a fixed amount of newlines.
myLabel.lineBreakMode = UILineBreakModeClip;
myLabel.text = [displayString stringByAppendingString:"\n\n\n\n"];
This solution won't work for everyone -- in particular, if you still want to show "..." at the end of your string if it exceeds the number of lines you're showing, you'll need to use one of the longer bits of code -- but for a lot of cases this'll get you what you need.
Instead of UILabel you may use UITextField which has vertical alignment option:
textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.userInteractionEnabled = NO; // Don't allow interaction
I've struggled with this one for a long time and I wanted to share my solution.
This will give you a UILabel that will autoshrink text down to 0.5 scales and vertically center the text. These options are also available in Storyboard/IB.
[labelObject setMinimumScaleFactor:0.5];
[labelObject setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];
Create a new class
LabelTopAlign
.h file
#import <UIKit/UIKit.h>
#interface KwLabelTopAlign : UILabel {
}
#end
.m file
#import "KwLabelTopAlign.h"
#implementation KwLabelTopAlign
- (void)drawTextInRect:(CGRect)rect {
int lineHeight = [#"IglL" sizeWithFont:self.font constrainedToSize:CGSizeMake(rect.size.width, 9999.0f)].height;
if(rect.size.height >= lineHeight) {
int textHeight = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(rect.size.width, rect.size.height)].height;
int yMax = textHeight;
if (self.numberOfLines > 0) {
yMax = MIN(lineHeight*self.numberOfLines, yMax);
}
[super drawTextInRect:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, yMax)];
}
}
#end
Edit
Here's a simpler implementation that does the same:
#import "KwLabelTopAlign.h"
#implementation KwLabelTopAlign
- (void)drawTextInRect:(CGRect)rect
{
CGFloat height = [self.text sizeWithFont:self.font
constrainedToSize:rect.size
lineBreakMode:self.lineBreakMode].height;
if (self.numberOfLines != 0) {
height = MIN(height, self.font.lineHeight * self.numberOfLines);
}
rect.size.height = MIN(rect.size.height, height);
[super drawTextInRect:rect];
}
#end
In Interface Builder
Set UILabel to size of biggest possible Text
Set Lines to '0' in Attributes Inspector
In your code
Set the text of the label
Call sizeToFit on your label
Code Snippet:
self.myLabel.text = #"Short Title";
[self.myLabel sizeToFit];
For Adaptive UI(iOS8 or after) , Vertical Alignment of UILabel is to be set from StoryBoard by Changing the properties
noOfLines=0` and
Constraints
Adjusting UILabel LefMargin, RightMargin and Top Margin Constraints.
Change Content Compression Resistance Priority For Vertical=1000` So that Vertical>Horizontal .
Edited:
noOfLines=0
and the following constraints are enough to achieve the desired results.
Create a subclass of UILabel. Works like a charm:
// TopLeftLabel.h
#import <Foundation/Foundation.h>
#interface TopLeftLabel : UILabel
{
}
#end
// TopLeftLabel.m
#import "TopLeftLabel.h"
#implementation TopLeftLabel
- (id)initWithFrame:(CGRect)frame
{
return [super initWithFrame:frame];
}
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines
{
CGRect textRect = [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines];
textRect.origin.y = bounds.origin.y;
return textRect;
}
-(void)drawTextInRect:(CGRect)requestedRect
{
CGRect actualRect = [self textRectForBounds:requestedRect limitedToNumberOfLines:self.numberOfLines];
[super drawTextInRect:actualRect];
}
#end
As discussed here.
What I did in my app was to set the UILabel's line property to 0 as well as to create a bottom constraint of the UILabel and make sure it is being set to >= 0 as shown in the image below.
Use textRect(forBounds:limitedToNumberOfLines:).
class TopAlignedLabel: UILabel {
override func drawText(in rect: CGRect) {
let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
super.drawText(in: textRect)
}
}
I wrote a util function to achieve this purpose. You can take a look:
// adjust the height of a multi-line label to make it align vertical with top
+ (void) alignLabelWithTop:(UILabel *)label {
CGSize maxSize = CGSizeMake(label.frame.size.width, 999);
label.adjustsFontSizeToFitWidth = NO;
// get actual height
CGSize actualSize = [label.text sizeWithFont:label.font constrainedToSize:maxSize lineBreakMode:label.lineBreakMode];
CGRect rect = label.frame;
rect.size.height = actualSize.height;
label.frame = rect;
}
.How to use? (If lblHello is created by Interface builder, so I skip some UILabel attributes detail)
lblHello.text = #"Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World!";
lblHello.numberOfLines = 5;
[Utils alignLabelWithTop:lblHello];
I also wrote it on my blog as an article:
http://fstoke.me/blog/?p=2819
I took a while to read the code, as well as the code in the introduced page, and found that they all try to modify the frame size of label, so that the default center vertical alignment would not appear.
However, in some cases we do want the label to occupy all those spaces, even if the label does have so much text (e.g. multiple rows with equal height).
Here, I used an alternative way to solve it, by simply pad newlines to the end of label (pls note that I actually inherited the UILabel, but it is not necessary):
CGSize fontSize = [self.text sizeWithFont:self.font];
finalHeight = fontSize.height * self.numberOfLines;
finalWidth = size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i = 0; i < newLinesToPad; i++)
{
self.text = [self.text stringByAppendingString:#"\n "];
}
I took the suggestions here and created a view which can wrap a UILabel and will size it and set the number of lines so that it is top aligned. Simply put a UILabel as a subview:
#interface TopAlignedLabelContainer : UIView
{
}
#end
#implementation TopAlignedLabelContainer
- (void)layoutSubviews
{
CGRect bounds = self.bounds;
for (UILabel *label in [self subviews])
{
if ([label isKindOfClass:[UILabel class]])
{
CGSize fontSize = [label.text sizeWithFont:label.font];
CGSize textSize = [label.text sizeWithFont:label.font
constrainedToSize:bounds.size
lineBreakMode:label.lineBreakMode];
label.numberOfLines = textSize.height / fontSize.height;
label.frame = CGRectMake(0, 0, textSize.width,
fontSize.height * label.numberOfLines);
}
}
}
#end
You can use TTTAttributedLabel, it supports vertical alignment.
#property (nonatomic) TTTAttributedLabel* label;
<...>
//view's or viewController's init method
_label.verticalAlignment = TTTAttributedLabelVerticalAlignmentTop;
I've found the answers on this question are now a bit out-of-date, so adding this for the auto layout fans out there.
Auto layout makes this issue pretty trivial. Assuming we're adding the label to UIView *view, the following code will accomplish this:
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
[label setText:#"Some text here"];
[label setTranslatesAutoresizingMaskIntoConstraints:NO];
[view addSubview:label];
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[label]|" options:0 metrics:nil views:#{#"label": label}]];
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[label]" options:0 metrics:nil views:#{#"label": label}]];
The label's height will be calculated automatically (using it's intrinsicContentSize) and the label will be positioned edge-to-edge horizontally, at the top of the view.
I've used a lot of the methods above, and just want to add a quick-and-dirty approach I've used:
myLabel.text = [NSString stringWithFormat:#"%#\n\n\n\n\n\n\n\n\n",#"My label text string"];
Make sure the number of newlines in the string will cause any text to fill the available vertical space, and set the UILabel to truncate any overflowing text.
Because sometimes good enough is good enough.
I wanted to have a label which was able to have multi-lines, a minimum font size, and centred both horizontally and vertically in it's parent view. I added my label programmatically to my view:
- (void) customInit {
// Setup label
self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
self.label.numberOfLines = 0;
self.label.lineBreakMode = UILineBreakModeWordWrap;
self.label.textAlignment = UITextAlignmentCenter;
// Add the label as a subview
self.autoresizesSubviews = YES;
[self addSubview:self.label];
}
And then when I wanted to change the text of my label...
- (void) updateDisplay:(NSString *)text {
if (![text isEqualToString:self.label.text]) {
// Calculate the font size to use (save to label's font)
CGSize textConstrainedSize = CGSizeMake(self.frame.size.width, INT_MAX);
self.label.font = [UIFont systemFontOfSize:TICKER_FONT_SIZE];
CGSize textSize = [text sizeWithFont:self.label.font constrainedToSize:textConstrainedSize];
while (textSize.height > self.frame.size.height && self.label.font.pointSize > TICKER_MINIMUM_FONT_SIZE) {
self.label.font = [UIFont systemFontOfSize:self.label.font.pointSize-1];
textSize = [ticker.blurb sizeWithFont:self.label.font constrainedToSize:textConstrainedSize];
}
// In cases where the frame is still too large (when we're exceeding minimum font size),
// use the views size
if (textSize.height > self.frame.size.height) {
textSize = [text sizeWithFont:self.label.font constrainedToSize:self.frame.size];
}
// Draw
self.label.frame = CGRectMake(0, self.frame.size.height/2 - textSize.height/2, self.frame.size.width, textSize.height);
self.label.text = text;
}
[self setNeedsDisplay];
}
Hope that helps someone!
FXLabel (on github) does this out of the box by setting label.contentMode to UIViewContentModeTop. This component is not made by me, but it is a component I use frequently and has tons of features, and seems to work well.
for anyone reading this because the text inside your label is not vertically centered, keep in mind that some font types are not designed equally. for example, if you create a label with zapfino size 16, you will see the text is not perfectly centered vertically.
however, working with helvetica will vertically center your text.
Subclass UILabel and constrain the drawing rectangle, like this:
- (void)drawTextInRect:(CGRect)rect
{
CGSize sizeThatFits = [self sizeThatFits:rect.size];
rect.size.height = MIN(rect.size.height, sizeThatFits.height);
[super drawTextInRect:rect];
}
I tried the solution involving newline padding and ran into incorrect behavior in some cases. In my experience, it's easier to constrain the drawing rect as above than mess with numberOfLines.
P.S. You can imagine easily supporting UIViewContentMode this way:
- (void)drawTextInRect:(CGRect)rect
{
CGSize sizeThatFits = [self sizeThatFits:rect.size];
if (self.contentMode == UIViewContentModeTop) {
rect.size.height = MIN(rect.size.height, sizeThatFits.height);
}
else if (self.contentMode == UIViewContentModeBottom) {
rect.origin.y = MAX(0, rect.size.height - sizeThatFits.height);
rect.size.height = MIN(rect.size.height, sizeThatFits.height);
}
[super drawTextInRect:rect];
}
If you are using autolayout, set the vertical contentHuggingPriority to 1000, either in code or IB. In IB you may then have to remove a height constraint by setting it's priority to 1 and then deleting it.
As long as you are not doing any complex task, you can use UITextView instead of UILabels.
Disable the scroll.
If you want the text to be displayed completely just user sizeToFit and sizeThatFits: methods
In swift,
let myLabel : UILabel!
To make your text of your Label to fit to screen and it's on the top
myLabel.sizeToFit()
To make your font of label to fit to the width of screen or specific width size.
myLabel.adjustsFontSizeToFitWidth = YES
and some textAlignment for label :
myLabel.textAlignment = .center
myLabel.textAlignment = .left
myLabel.textAlignment = .right
myLabel.textAlignment = .Natural
myLabel.textAlignment = .Justified
This is an old solution, use the autolayout on iOS >= 6
My solution:
Split lines by myself (ignoring label wrap settings)
Draw lines by myself (ignoring label alignment)
#interface UITopAlignedLabel : UILabel
#end
#implementation UITopAlignedLabel
#pragma mark Instance methods
- (NSArray*)splitTextToLines:(NSUInteger)maxLines {
float width = self.frame.size.width;
NSArray* words = [self.text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSMutableArray* lines = [NSMutableArray array];
NSMutableString* buffer = [NSMutableString string];
NSMutableString* currentLine = [NSMutableString string];
for (NSString* word in words) {
if ([buffer length] > 0) {
[buffer appendString:#" "];
}
[buffer appendString:word];
if (maxLines > 0 && [lines count] == maxLines - 1) {
[currentLine setString:buffer];
continue;
}
float bufferWidth = [buffer sizeWithFont:self.font].width;
if (bufferWidth < width) {
[currentLine setString:buffer];
}
else {
[lines addObject:[NSString stringWithString:currentLine]];
[buffer setString:word];
[currentLine setString:buffer];
}
}
if ([currentLine length] > 0) {
[lines addObject:[NSString stringWithString:currentLine]];
}
return lines;
}
- (void)drawRect:(CGRect)rect {
if ([self.text length] == 0) {
return;
}
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, self.textColor.CGColor);
CGContextSetShadowWithColor(context, self.shadowOffset, 0.0f, self.shadowColor.CGColor);
NSArray* lines = [self splitTextToLines:self.numberOfLines];
NSUInteger numLines = [lines count];
CGSize size = self.frame.size;
CGPoint origin = CGPointMake(0.0f, 0.0f);
for (NSUInteger i = 0; i < numLines; i++) {
NSString* line = [lines objectAtIndex:i];
if (i == numLines - 1) {
[line drawAtPoint:origin forWidth:size.width withFont:self.font lineBreakMode:UILineBreakModeTailTruncation];
}
else {
[line drawAtPoint:origin forWidth:size.width withFont:self.font lineBreakMode:UILineBreakModeClip];
}
origin.y += self.font.lineHeight;
if (origin.y >= size.height) {
return;
}
}
}
#end

Calculate Cell height on basis of label text + image

I have created a custom cell that have IMAGE view, and two label's the data of labels's are populated from a plist file, the data is populated properly but on front end the cell didn't show the data properly, the label cut's the data. I am using Uilabel view's.
Please have a view to my code, i have search over internet and followed some tutorial's as well but nothing work's.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Customviewcell *cell=[tableView dequeueReusableCellWithIdentifier:#"cell"];
UIImage * image = [UIImage imageNamed:justThumbs[indexPath.row]];
cell.CustomTitle.text=justTitles[indexPath.row];
cell.CustomTitle.numberOfLines =0;
[cell.CustomTitle sizeToFit];
cell.CustomDes.text=justDesc[indexPath.row];
cell.CustomDes.numberOfLines=0;
[cell.CustomDes sizeToFit];
[cell.CustomTitle layoutIfNeeded];
[cell.CustomDes layoutIfNeeded];
[cell layoutIfNeeded];
cell.Customimage.image=image;
return cell;
}
Code for calculating the height as per stackoverflow different question's answer's.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Calculate Height Based on a cell
if (!self.customcell) {
self.customcell=[tableView dequeueReusableCellWithIdentifier:#"cell"];
}
// Configure Cell
UIImage * image = [UIImage imageNamed:justThumbs[indexPath.row]];
self.customcell.CustomTitle.text=justTitles[indexPath.row];
self.customcell.CustomTitle.numberOfLines=0;
[self.customcell.CustomTitle sizeToFit];
self.customcell.CustomDes.text=justDesc[indexPath.row];
self.customcell.CustomDes.numberOfLines=0;
[self.customcell.CustomDes sizeToFit];
self.customcell.Customimage.image=image;
//Layout Cell
//Get Hieght for the cell
if([[UIDevice currentDevice]userInterfaceIdiom]==UIUserInterfaceIdiomPhone)
{
if ([[UIScreen mainScreen] bounds].size.height == 568)
{
CGRect frame = [NSString setAttributeWithString:self.customcell.CustomTitle.text withLineSpacing:0.2 withSize:CGSizeMake(270, 999999999) withFont:self.customcell.CustomTitle.font withLabel:self.customcell.CustomTitle setLabelTextColor:self.customcell.CustomTitle.textColor setTextAlignment:self.customcell.CustomTitle.textAlignment];
self.customcell.CustomTitle.height.constant = frame.size.height;
frame = [NSString setAttributeWithString:self.customcell.CustomDes.text withLineSpacing:0.3 withSize:CGSizeMake(150, 999999999) withFont:self.customcell.CustomDes.font withLabel:self.customcell.CustomDes setLabelTextColor:self.customcell.CustomDes.textColor setTextAlignment:self.customcell.CustomDes.textAlignment];
self.customcell.CustomDes.height.constant = frame.size.height;
}
else{
CGRect frame = [NSString setAttributeWithString:self.customcell.CustomTitle.text withLineSpacing:1 withSize:CGSizeMake(337, 999999999) withFont:self.customcell.CustomTitle.font withLabel:self.customcell.CustomTitle setLabelTextColor:self.customcell.CustomTitle.textColor setTextAlignment:self.customcell.CustomTitle.textAlignment];
self.customcell.CustomTitle.height.constant = frame.size.height;
frame = [NSString setAttributeWithString:self.customcell.CustomDes.text withLineSpacing:1 withSize:CGSizeMake(227, 999999999) withFont:self.customcell.CustomDes.font withLabel:self.customcell.CustomDes setLabelTextColor:self.customcell.CustomDes.textColor setTextAlignment:self.customcell.CustomDes.textAlignment];
self.customcell.CustomDes.height.constant = frame.size.height;
}
}
[self.customcell layoutIfNeeded];
// CGFloat height = self.customcell.CustomTitle.height.constant+self.customcell.CustomDes.height.constant+189;
CGFloat height = [self.customcell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
//Add padding of 1
return height;
}
Used Github opensource library to solve the issue but didn't worked.
https://github.com/punchagency/line-height-tool
Issue still remain's, text of label's cut off, content hanging is at Required and Content is at 1000 horizontal + vertical..
Please help..
Thanks allot.
You can get the height of a string in certain bounds with the NSString method boundingRectWithSize, like
NSString* text = #"Test text";
CGRect textRect = [text boundingRectWithSize:CGSizeMake(300, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName : [UIFont systemFontOfSize:12]}
context:nil];
CGFloat textHeight = ceilf(textRect.size.height);
Use your own font and font size, and you can also add other attributes to the attributes dictionary if necessary.
Use following method to calculate your UILabel height:
add also use this method in your class.
- (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;}
Update your heightForRowAtIndexPath Method as per your requirement:
calculate all UI Height and return.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
Customviewcell *cell=[tableView dequeueReusableCellWithIdentifier:#"cell"];
CGFloat totalHeight = 0;
cell.CustomTitle.text=justTitles[indexPath.row];
//get label Height
totalHeight += [Helper getLabelHeight:cell.CustomTitle];
cell.Customimage.image = [UIImage imageNamed:justThumbs[indexPath.row]];
CGFloat imageHeight = cell.Customimage.frame.size.height; //or Add image height here
totalHeight += imageHeight;
return totalHeight;}
In iOS8+ you can use self sizing cells:
http://www.appcoda.com/self-sizing-cells/
Auto Resizing of Cell is available in Ios 8.0, The issue my deployment target was ios 7.0, which is causing the layout issues.
Please refer to these articles:
http://www.appcoda.com/self-sizing-cells/
The Code is in swift but need to do same thing's in objective c as well.
This will also help's you.
http://useyourloaf.com/blog/self-sizing-table-view-cells.html

UITableView cell width on iOS 8 stuck at 320pt

I am currently trying to create a simple UITableView with custom cells without using storyboard.
I'm getting an issue on the iPhone 6 simulator where the table view has a width of 375 (as it should), but the cells inside are getting a width of 320.
The number 320 is nowhere to be found in the project as I am not hard coding it. When I am setting the background colour of the cell, it extends the full width of 375, but I need to align an image to the right, which only aligns 320 across as shown in the photo below.
I'm not sure if it's because I'm missing constraints or if there's a bug. Any help is appreciated, thanks!
Code to set up table:
- (TBMessageViewCell *)getMessageCellforTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"MessageCell";
TBMessageViewCell *cell = (TBMessageViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[TBMessageViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
[cell createSubviews];
}
// Set the new message and refresh
[cell setMessage:self.viewModel.messages[indexPath.row]];
[cell populateSubviews];
cell.backgroundColor = [UIColor blueColor];
NSLog(#"cell Width: %f", cell.contentView.frame.size.width);
return cell;
}
Complete TBMessageViewCell:
#implementation TBMessageViewCell
const CGFloat MARGIN = 10.0f;
const CGFloat AVATAR_SIZE = 40.0f;
-(id)initWithStyle:(UITableViewCellStyle *)style reuseIdentifier:(NSString *)reuseIdentifier
{
if(self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]){
}
// Sets background and selected background color
self.backgroundColor = [UIColor clearColor];
UIView *selectionColor = [[UIView alloc] init];
selectionColor.backgroundColor = [UIColor clearColor];
self.selectedBackgroundView = selectionColor;
return self;
}
- (void)populateSubviews
{
// Set the message body
[self.messageBodyLabel setText:self.message.body];
[self.messageBodyLabel setTextAlignment:NSTextAlignmentRight];
CGRect bodyFrame = CGRectMake(MARGIN, MARGIN, self.frame.size.width - (AVATAR_SIZE + (MARGIN * 3)), self.frame.size.height);
// Calculates the expected frame size based on the font and dimensions of the label
// FLT_MAX simply means no constraint in height
CGSize maximumLabelSize = CGSizeMake(bodyFrame.size.width, FLT_MAX);
CGRect textRect = [self.message.body boundingRectWithSize:maximumLabelSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:self.messageBodyLabel.font}
context:nil];
bodyFrame.size.height = textRect.size.height;
// Setup the new avatar frame (Right aligned)
CGRect avatarFrame = CGRectMake(bodyFrame.size.width + (MARGIN * 2), MARGIN, AVATAR_SIZE, AVATAR_SIZE);
// Align to the LEFT side for current user's messages
if ([[TBConfig userID] isEqualToString:self.message.user.userID]) {
// Set avatar to left if it's me
avatarFrame.origin.x = MARGIN;
bodyFrame.origin.x = AVATAR_SIZE + (MARGIN * 2);
[self.messageBodyLabel setTextAlignment:NSTextAlignmentLeft];
}
self.avatar.frame = avatarFrame;
self.avatar.layer.cornerRadius = self.avatar.frame.size.width/2;
self.messageBodyLabel.frame = bodyFrame;
// Set the new cell height on the main Cell
CGFloat cellHeight = MAX(bodyFrame.size.height, self.frame.size.height) + MARGIN;
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, cellHeight);
// Set the new Profile avatar
if (![self.avatar.profileID isEqualToString:self.message.user.facebookID]) {
[self.avatar setProfileID:nil];
[self.avatar setProfileID:self.message.user.facebookID];
}
}
- (void)createSubviews
{
self.messageBodyLabel = [[UILabel alloc] init];
self.messageBodyLabel.textColor = [UIColor whiteColor];
self.messageBodyLabel.lineBreakMode = NSLineBreakByWordWrapping;
self.messageBodyLabel.numberOfLines = 0;
[self addSubview:self.messageBodyLabel];
// Creates the avatar
self.avatar = [[FBProfilePictureView alloc] init];
[self.avatar setPictureCropping:FBProfilePictureCroppingSquare];
[self addSubview:self.avatar];
}
You're printing the size of the cell before it has been added to the display — before it has been sized. It doesn't yet know the size of tableview it will be added to.
The cells will be given an appropriate frame when added to the display.
EDIT: oh, and you probably don't want that cellIdentifier to be static. You probably wanted *const.
Don't know if you have found the answer. I faced the same problem when I was trying to subclass UITableViewCell and add custom subviews programmatically without using xib.
Finally the solution worked for me is to use [[UIScreen mainScreen] applicationFrame] instead of self.frame when calculating subviews' frames.
The proper way to solve this is to perform your layout in the layoutSubviews method.
In your case, simply call "populateSubviews" within "layoutSubviews" method, like this:
- (void)layoutSubviews {
[super layoutSubviews];
[self populateSubviews];
}
... but before doing this I would recommend that you do content population in a separate method (ie, calls to label.text = ...), and place all layout-affecting calls (ie, label.frame = ...) below [super layoutSubviews] in the method above.
That would result in something like:
- (void)layoutSubviews {
[super layoutSubviews];
CGRect bodyFrame = CGRectMake(MARGIN, MARGIN, self.frame.size.width - (AVATAR_SIZE + (MARGIN * 3)), self.frame.size.height);
// Calculates the expected frame size based on the font and dimensions of the label
// FLT_MAX simply means no constraint in height
CGSize maximumLabelSize = CGSizeMake(bodyFrame.size.width, FLT_MAX);
CGRect textRect = [self.message.body boundingRectWithSize:maximumLabelSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:self.messageBodyLabel.font}
context:nil];
bodyFrame.size.height = textRect.size.height;
// .. the rest of your layout code here ..
}
- (void)populateSubviews {
[self.messageBodyLabel setText:self.message.body];
// .. the rest of your code here ..
}
After you set your avatar frame in:
self.avatar.frame = avatarFrame;
self.avatar.layer.cornerRadius = self.avatar.frame.size.width/2;
self.messageBodyLabel.frame = bodyFrame;
write
self.avatar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin;
It should hook your avatar image to the right margin and leave the left margin as flexible.
Select your table view cell's connection inspector and check if you haven't connected editingAccessoryView by mistake
While this answer may not be as straightforward as you'd expect UIKit to deliver, due to a bug in UITableView - when doing things programmatically - you gotta get your hands dirty. Xib lovers - be warned - this answer isn't for you. It's probably just working.
In later IOS versions, let's hope this problem get resolved. The problem is uitableview is hardcoding the dimensions of the cell to 320. In one project I had - I was forcing the frame size to to fix this. N.B. this has problems with splitview controller on iPad.
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"cell frame width:%f",cell.frame.size.width); // you should see <- 320! WTH
// you can crudely correct this here
cell.frame = CGRectMake(0,0,[[UIScreen mainScreen] applicationFrame].size.width,CELL_HEIGHT);
}
Another option - that's working better for me is a local width variable to reference. I know this code is not ideal - but it works consistently.
#interface AbtractCustomCell : UITableViewCell {
float width;
}
#end
#implementation AbtractCustomCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
//optimise speed
// [self setOpaque:YES];
if ([SDiPhoneVersion deviceSize] == iPhone47inch) {
width = 375;
}
else if ([SDiPhoneVersion deviceSize] == iPhone47inch) {
width = 414;
}
else {
width = 320; // hardcode iphone 4/5 /ipad2
}
}
return self;
}
#end
then you have the option to make TBMessageViewCell a subclass of this AbstractCustomCell which will have this variable there for you. Instead of using self.contentView.bounds.size.width / self.bounds.size.width just use width.
I'm programmatically layout without Storyboard and facing same issue, and I need to return the cell upon cellForRowAt indexPath which in turn it still didnt manage to get the container size yet.
And I manage to solve it by move the layout codes into willDisplay cell delegate.

How to adjust a label size to fit the length of the text

I have searched the solution to this in the past QAs, but could not find the right one.
Does anyone know how to adjust aUILabel size dynamically to fit the text length?
I have uploaded the screen shot of what I don't want(1st line) and what I want(2nd line).
I'd appreciate any clues, advice or code sample. Thank you.
What you are searching is the UILabel method sizeToFit
I can try to explain to you, but the best answer to know how to work with UILabel is that: https://stackoverflow.com/a/1054681/666479
Xcode 8 and iOS 10
This is quite easy to do with Auto Layout. No need to do anything in code.
Use Auto Layout
Use auto layout to pin each label's top and left edges only. Don't add constraints for the width and height. The view's intrinsic content size will take care of that.
Here is what the constraints look like:
Code
There is nothing at all special about the code. No need to use sizeToFit or anything like that.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var labelOne: UILabel!
#IBOutlet weak var labelTwo: UILabel!
#IBOutlet weak var labelThree: UILabel!
#IBAction func changeTextButtonTapped(_ sender: UIButton) {
labelOne.text = "David"
labelTwo.text = "met"
labelThree.text = "her"
}
}
Notes
This answer has been retested with Xcode 8, iOS 10, and Swift 3.
See my other answer if you want multi-line resizing.
Thanks to this answer for setting me on the right track.
Use This Extended UILabel class:
//
// UILabelExtended.h
//
// Created by Prateek on 6/18/11.
#import <Foundation/Foundation.h>
/* **********************************************************************************************
This class inherit the class UILabel and extend the features of UILabel.
********************************************************************************************** */
#interface UILabelExtended : UILabel {
__unsafe_unretained id customDelegate;
id objectInfo;
SEL selector;
}
#property (nonatomic,assign) SEL selector;;
#property (nonatomic,assign) id customDelegate;
#property (nonatomic,retain) id objectInfo;
#end
#interface UILabel(UILabelCategory)
- (void)setHeightOfLabel;
- (void)setWidthOfLabel;
- (void)setHeightOfLabelWithMaxHeight:(float)maxHeight;
- (void)setWidthOfLabelWithMaxWidth:(float)maxWidth ;
#end
UILabelExtended.m
//
// Created by Prateek on 6/18/11.
//
#import "UILabelExtended.h"
#implementation UILabelExtended
#synthesize selector,customDelegate, objectInfo;
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(self.selector)
if([self.customDelegate respondsToSelector:self.selector]) {
[self.customDelegate performSelector:self.selector withObject:self];
return;
}
}
- (void)dealloc {
self.customDelegate = nil;
self.selector = NULL;
self.objectInfo = nil;
}
#end
#implementation UILabel(UILabelCategory)
- (void)setHeightOfLabel {
UILabel* label = self;
//get the height of label content
CGFloat height = [label.text sizeWithFont:label.font constrainedToSize:CGSizeMake(label.bounds.size.width, 99999) lineBreakMode:NSLineBreakByWordWrapping].height;
//set the frame according to calculated height
CGRect frame = label.frame;
if([label.text length] > 0) {
frame.size.height = height;
}
else {
frame.size.height = 0;
}
label.frame = frame;
}
- (void)setWidthOfLabel {
UILabel* label = self;
//get the height of label content
CGFloat width = [label.text sizeWithFont:label.font constrainedToSize:CGSizeMake(99999, label.bounds.size.height) lineBreakMode:NSLineBreakByWordWrapping].width;
//set the frame according to calculated height
CGRect frame = label.frame;
if([label.text length] > 0) {
frame.size.width = width+5;
}
else {
frame.size.width = 0;
}
label.frame = frame;
}
- (void)setHeightOfLabelWithMaxHeight:(float)maxHeight {
UILabel* label = self;
//get the height of label content
CGFloat height = [label.text sizeWithFont:label.font constrainedToSize:CGSizeMake(label.bounds.size.width, maxHeight) lineBreakMode:NSLineBreakByWordWrapping].height;
//set the frame according to calculated height
CGRect frame = label.frame;
if([label.text length] > 0) {
if (height > maxHeight) {
frame.size.height = maxHeight;
}
else {
frame.size.height = height;
}
}
else {
frame.size.height = 0;
}
label.frame = frame;
}
- (void)setWidthOfLabelWithMaxWidth:(float)maxWidth {
UILabel* label = self;
//get the height of label content
CGFloat width = [label.text sizeWithFont:label.font constrainedToSize:CGSizeMake(99999, label.bounds.size.height) lineBreakMode:NSLineBreakByWordWrapping].width;
//set the frame according to calculated height
CGRect frame = label.frame;
if([label.text length] > 0) {
if (width > maxWidth) {
frame.size.width = maxWidth;
}
else {
frame.size.width = width;
}
}
else {
frame.size.width = 0;
}
label.frame = frame;
}
#end
Use Methods:
1) set text of UILabel
2) [yourLBLObj setHeightOfLabel]; or [yourLBLObj setWidthOfLabel];
It will automatically set Height or Width according to text.
you get simply calculate UILabel width for string size,try this simple code for set UILabel size
// Single line, no wrapping;
CGSize expectedLabelSize = [string sizeWithFont:yourFont];
// you get width,height in expectedLabelSize;
//expectedLabelSize.width,expectedLabelSize.height
try this
NSString *text1 = [NSString stringWithFormat:#"%#",commentText];
CGSize constraint1 = CGSizeMake(280, 2000);
CGSize size1 = [text1 sizeWithFont:[UIFont systemFontOfSize:12] constrainedToSize:constraint1 lineBreakMode:UILineBreakModeWordWrap];
UILabel *lblComment = [[UILabel alloc] initWithFrame:CGRectMake(posx,posy,size1.width,size1.height)] ;
lblComment.lineBreakMode = UILineBreakModeWordWrap;
lblComment.numberOfLines = size1.height/15;
[lblComment setFont:[UIFont systemFontOfSize:12]];
lblComment.text = text1;
lblComment.tag = shareObjC.userId;
[lblComment setNeedsDisplay]
All you need to just put 2 lines code
lbl1.numberOfLines = 0
lbl1.sizeToFit()
It will manage label width as per its content
Hope it helps others :)
You can use this piece of code for calculate label width and set it
CGSize expectedLabelSize = [name sizeWithFont:yourfont constrainedToSize:maximumLabelSize];
// you can get width width height from expectedLabelSize and set accordingly
Currently i m working on IOS-8 and there is small changes i made to #Suragch answer (Need to use auto layout to make this work)
Below is the step and screenshot in result :
Place the textview inside UIView and not directly inside content-view
in storyboard
Disable scroll of textview from storyboard
Add Leading and top constraints as i have added to view in below
screenshot(Trailing constraint is optional but i have added )
There is no need to add code in swift this can be done in storyboard itself.
Result :

NSString sizeWithFont:constrainedToSize: computing wrong height

I have a Subtitle style UITableViewCell which height changes dynamically depending on the length of the text for each field. The problem is that the textLabel's height (CGSize size) does not increase if the label has multiple lines.
The weird part is that the detailTextLabel's height is increasing as it should (CGSize size2). The code to calculate both heights are identical.
Here is my function:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
SETLISTFMNS0Song *song = [[[selectedSetlist.sets objectAtIndex:indexPath.section] songs]objectAtIndex:indexPath.row];
CGSize size = [song.name sizeWithFont:[UIFont fontWithName:setlistFont size:labelFontSize] constrainedToSize:CGSizeMake(self.setsTable.bounds.size.width, CGFLOAT_MAX)];
NSLog(#"Label: \"%#\" \tLabel Size: %f W %f H", song.name, size.width, size.height);
NSMutableString *detail;
if ([song cover]) {
detail = [[NSMutableString alloc] initWithFormat:#"(%# cover)", [[song cover] name]];
}
if ([song with]) {
if (!detail) {
detail = [[NSMutableString alloc] initWithFormat:#"(with %#)", [[song with] name]];
}
else {
[detail appendFormat:#" (with %#)", [[song with] name]];
}
}
if ([song info]) {
if (!detail) {
detail = [[NSMutableString alloc] initWithFormat:#"(%#)", [song info]];
}
else {
[detail appendFormat:#" (%#)", [song info]];
}
}
if (detail.length != 0) {
CGSize size2 = [detail sizeWithFont:[UIFont fontWithName:setlistFont size:detailFontSize] constrainedToSize:CGSizeMake(self.setsTable.bounds.size.width, CGFLOAT_MAX)];
size.height += size2.height;
NSLog(#"Detail Label: \"%#\" \tDetail Label Size: %f W %f H", detail, size2.width, size2.height);
}
return size.height + 5;
}
I am also setting both textLabel's numberOfLines property to 0 in cellForRowAtIndexPath to support multiple lines:
cell.textLabel.numberOfLines = 0;
cell.detailTextLabel.numberOfLines = 0;
UPDATE: Thanks to #josh I now understand why this is happening. I had the width constraints set to the width of the UITableView, which is too wide. Anyone know how to find the width of the UILabel before it is created? HA!
Thanks!
How long are the strings you're calculating the size against? I ask because you're sizeWithFont:constrainedToSize: function is constraining to the full width of the cell, but your labels are probably not the full width of the cell.
What I suspect is happening is song.info is just short enough that it would fit on one line if the line were the full width of the cell, and that your song detail is long enough that it is calculating the correct number of lines, but not so long as to exceed the calculated height.
All of that to say, I think what you need to do is find out the widths of textLabel and detailTextLabel and set your constraints to those values.
Update - A way of calculating label widths
Since the width of the labels inside of a cell are dependent on the width of the cell, and since cell's aren't created at the time heightForRowAtIndexPath: is called, we need to come up with a way to know the labels' widths before they the widths are set. The only way to do this is to set the widths ourselves. Here's how I would do it:
MyCell.h
#import <UIKit/UIKit.h>
#interface MyCell : UITableViewCell
+ (CGFloat)textLabelWidthForCellOfWidth:(CGFloat)cellWidth;
#end
MyCell.m
#import "MyCell.h"
#implementation MyCell
- (void)layoutSubviews {
[super layoutSubviews];
CGRect frame = self.textLabel.frame;
frame.size.width = [MyCell textLabelWidthForCellOfWidth:self.frame.size.width];
self.textLabel.frame = frame;
}
+ (CGFloat)textLabelWidthForCellOfWidth:(CGFloat)cellWidth {
// This calculation can be as complex as necessary to account for all elements that affect the label
return cellWidth - 20;
}
#end
Then in your heightForRowAtIndexPath: implementation you can call the same class method:
CGFloat labelWidth = [MyCell textLabelWidthForCellOfWidth:self.tableView.frame.size.width]; // Since non-grouped cells are the full width of the tableView
CGSize size2 = [detail sizeWithFont:[UIFont fontWithName:setlistFont size:detailFontSize] constrainedToSize:CGSizeMake(labelWidth, CGFLOAT_MAX)];
You would create a separate Class Method (+) for each label that you need to reference.
as looking at your code i found you are missing to define "lineBreakMode".
CGSize theSize = [song.name sizeWithFont:[UIFont fontWithName:setlistFont size:labelFontSize] constrainedToSize:CGSizeMake(self.setsTable.bounds.size.width, CGFLOAT_MAX) lineBreakMode:UILineBreakModeWordWrap];
update your code with this. i hope it will fix your problem. Good luck

Resources