Change font size in UILabel depending on string length - objective-c

Let's say I have a UILabel which covers the entire window of the app. In this label is displayed random text with different lengths. Is it possible to change the text font size dependant on the text length?

Yes, UILabel can do that for you, just do:
theLabel.adjustsFontSizeToFitWidth = YES;
theLabel.minimumFontSize = MIN_FONT_SIZE;
Attention to this (from the documentation):
This property is effective only when the numberOfLines property is set
to 1.

I created a category method at one point. You basically feed it a rectangle and it will return a font that fits. Maybe you can glean something from the following crude example:
- (UIFont *)fontSizeForRect:(CGRect)rect withFont:(UIFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode minFontSize:(CGFloat)minFontSize
{
CGFloat fontSize = [font pointSize];
UIFont *tempFont = [UIFont fontWithName:[font fontName] size:[font pointSize]];
CGFloat acceptableFontSize = fontSize;
while (fontSize > minFontSize)
{
UIFont *testFont = [UIFont fontWithName:[tempFont fontName] size:fontSize];
CGSize sizeWithTestFont = [self sizeWithFont:testFont constrainedToSize:CGSizeMake(rect.size.width, 99999.0) lineBreakMode:lineBreakMode];
if (sizeWithTestFont.height > rect.size.height)
fontSize -= 1.0f; //Shrink the font size by a point
else
{
//Fits. Use it.
acceptableFontSize = fontSize;
break;
}
}
return [UIFont fontWithName:[font fontName] size:acceptableFontSize];
}

Related

sizeWithFont: and sizeWithAttributes: don't work for a long single line

I'm trying to set a dynamic height for cells in my table, height should be based on a text length and a max width.
The problem appears when this text comes in a single line, without line separators. Doesn't matter how large the text is, if there are no line separators it detects that text fits in a single line so my cell height doesn't increase.
Am I doing something wrong? How can I achieve it? Thanks.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat cellheight = 35.0f; // BASE
NSString *text = #"...";
if (text) {
UIFont *font = (indexPath.row == 0) ? [UIFont systemFontOfSize:14] : [UIFont systemFontOfSize:12];
CGSize constraintSize = CGSizeMake(self.tableView.frame.size.width, CGFLOAT_MAX);
if (IS_EARLIER_THAN_IOS7) {
CGSize size = [text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:NSLineBreakByCharWrapping];
cellheight += size.height;
} else {
CGSize size = [text sizeWithAttributes:#{NSFontAttributeName: [UIFont systemFontOfSize:12.0f]}];
CGSize adjustedSize = CGSizeMake(ceilf(size.width), ceilf(size.height));
cellheight += adjustedSize.height;
}
return (indexPath.row == 0) ? cellheight + 40.0f : cellheight;
}
}
- (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(NSLineBreakMode)lineBreakMode NS_DEPRECATED_IOS(2_0, 7_0, "Use -boundingRectWithSize:options:attributes:context:") __TVOS_PROHIBITED; // NSTextAlignment is not needed to determine size
You should use "boundingRectWithSize:options:attributes:context:" not "sizeWithAttributes:".
This is a sample
CGSize size = [text boundingRectWithSize:CGSizeMake(_myTableView.frame.size.width, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:[UIFont systemFontOfSize:14]} context:nil].size;
There is a way more easy way to do that:
First set the text to the UILabel, and set all the required font, size. etc. Then call sizeThatFits method on the label.
CGSize sizze =[itemLabel sizeThatFits:CGSizeMake(itemNameLabelWidth, CGFLOAT_MAX)];
Also dont forget to set numberOfLines and lineBreakMode before calling sizeThatFits:
itemLabel.numberOfLines=0;
itemLabel.lineBreakMode=NSLineBreakByWordWrapping;
Note 1 : calling sizeThatFits does not set the new frame to the UILabel, it just calculates and returns the new frame. You have to then set the frame to the label by adding x and y origin values. So that becomes :
CGSize sizze =[itemLabel sizeThatFits:CGSizeMake(itemNameLabelWidth, CGFLOAT_MAX)];
CGRect namelabelFrame = itemLabel.frame;
namelabelFrame.size = sizze;
itemLabel.frame = namelabelFrame;
Note 2 : This code is okay in cellForRowAtIndexPath:, but when calculating the height inside heightForRowAtIndexPath: you may want to optimize this code a little bit. As you don't have the cell to work with you might initialize a UILabel object and perform this code on it to estimate the height. But having UIView initializations inside heightForRowAtIndexPath: is not a good idea as they can significantly affect performance while scrolling.
So what you do is have an already initialised (and all formatting applied) UILabel as a class variable and reuse that for height calculation.

iOS 7 - UITextView size font to fit all text into view (no scroll)

I am trying to find a non-deprecated method to size the font of a textview down so that all text fits in the textview without requiring scrolling.
The method 'sizeWithFont' is deprecated and I want to ensure best practices, and XCode says to use 'boundingRectWithSize' but not sure how to use this to size a font down so that all text fits.
Any suggestions?
And NO I can not use a UILabel instead. I need to have the text vertically aligned at the top and UILabel does not do this.
This worked Pre-iOS 7:
CGFloat fontSize;
CGFloat minSize;
if([deviceType isEqualToString:#"iPad"] || [deviceType isEqualToString:#"iPad Simulator"]){
fontSize = 40;
minSize = 15;
}
else{
fontSize = 18;
minSize = 8;
}
while (fontSize > minSize)
{
CGSize size = [quote sizeWithFont:[UIFont fontWithName:#"Interstate" size:fontSize] constrainedToSize:CGSizeMake(newView.frame.size.width, 10000)];
if (size.height <= newView.frame.size.height) break;
fontSize -= 1.0;
}
Solution 1
Your problem can be solved by simply replacing sizeWithFont: constrainedToSize: with :
boundingRectWithSize:CGSizeMake(newView.frame.size.width, FLT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:[UIFont fontWithName:#"Interstate" size:fontSize]}
context:nil];
Solution 2
The sizeThatFits method can be used to address this problem like this:
while (fontSize > minSize && [newView sizeThatFits:(CGSizeMake(newView.frame.size.width, FLT_MAX))].height >= newView.frame.size.height ) {
fontSize -= 1.0;
newView.font = [tv.font fontWithSize:fontSize];
}
I hope one of these solutions solve your problem. Cheers!
I had to do the same but then with programmatically added AutoLayout constraints (NSLayoutConstraint). Because of the constraints the contentSize wasn't correct for the most time causing the UITextView to scroll :/ To still get the correct font size I ended up just creating a new UITextView for the sake of doing some good ol' trial and error testing and getting it through there.
Pretty simple but like with everything you just gotta come up with it :)
NSInteger fontSize = 200;
UITextView *testTextView = [[UITextView alloc] init];
testTextView.text = self.myRealTextView.text;
testTextView.font = [UIFont fontWithName:#"AldotheApache" size:fontSize];
while ([testTextView sizeThatFits:(CGSizeMake(self.myRealTextView.frame.size.width, FLT_MAX))].height >= self.myRealTextView.frame.size.height ) {
fontSize -= 0.5;
testTextView.font = [UIFont fontWithName:#"AldotheApache" size:fontSize];
}
NSLog(#"Correct font size is: %ld",(long)fontSize);
self.myRealTextView.font = [UIFont fontWithName:#"AldotheApache" size:fontSize];
Try UITextView's sizeThatFits. You can probably use it the following way:
//Set text content of UITextView
//...
while (fontSize > minSize) {
// Set font size of UITextView
CGSize size = [textView sizeThatFits:(CGSizeMake(textView.frame.size.width, FLT_MAX)];
if (size.height <= newView.frame.size.height) break;
fontSize -= 1.0;
}
UITextView is subclass of UIScrollView -> so you can check the contentSize of the scrollable area after you set your text or font into textView.
textView.text = quote;
do
{
textView.font = [UIFont fontWithName:#"Interstate" size:fontSize];
[textView layoutIfNeeded];
if (textView.contentSize.height <= newView.frame.size.height) {
break;
}
fontSize -= 1.0;
} while (fontSize >= minSize);
That should work... Probably it would work even without [textView layoutIfNeeded].
Swift 5+
var fontSize = Font.Size.section.rawValue
let minSize = Font.Size.large.rawValue
while fontSize > minSize {
let textViewSize = CGSize(width: textView.frame.size.width, height: textView.frame.size.height)
let size = textView.sizeThatFits(textViewSize)
if size.height <= textViewSize.height {
break
}
fontSize -= 1.0
}
textView.font = Font.primary.of(size: fontSize)

UILabel text gets cut off

I'm trying to dynamicly set label size. It works in a strange way, i get some of the text cut off.
I first set my label text and then try to resize it like this way.
_switch2Label.text = #"Call on alarm, there will be no call if other user of alarm system will recieve an alarm call and confirm (answer) it by pressing 0#";
_switch2Label.numberOfLines = 0;
[self newFrame:_switch2Label];
- (void) newFrame:(UILabel *) label
{
CGSize maxSize = self.view.bounds.size;
maxSize.width = maxSize.width - 30;
CGSize labelSize = [label.text sizeWithFont:label.font constrainedToSize:maxSize lineBreakMode:label.lineBreakMode];
CGRect newFrame = label.frame;
newFrame.size.height = labelSize.height;
label.frame = newFrame;
}
I only get three lines of text, while five is needed for this label. Maybe anyone could see my mistake here? If I add more text to label it gets shown, yet still about two lines of the label text gets cutt off.
I have changed your method...Please check it..it may help you..
- (void) newFrame:(UILabel *) label
{
CGSize constraint = CGSizeMake(300, 1000.0f);
CGSize size_txt_overview1 = [label.text sizeWithFont:[UIFont fontWithName:#"Arial Rounded MT Bold" size:15] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
label.frame = CGRectMake(20,20, size_txt_overview1.width, size_txt_overview1.height+15);
}
_switch2Label.text = #"Call on alarm, there will be no call if other user of alarm system will recieve an alarm call and confirm (answer) it by pressing 0#,";
_switch2Label.numberOfLines = 0;
[self newFrame:_switch2Label];
- (void) newFrame:(UILabel *) label
{
CGSize maximumSize = CGSizeMake(label.frame.size.width, 10000);
//maxSize.width = maxSize.width - 30;
CGSize labelSize = [label.text sizeWithFont:label.font constrainedToSize:maximumSize lineBreakMode:UILineBreakModeWordWrap];
CGRect newFrame = label.frame;
newFrame.size.height = labelSize.height;
label.frame = newFrame;
}
Use this code blocks , may help u.
Why programatically resizing the label? Is this something you cannot do in IB or using autorezizeMask?
The label's constraint size isn't getting calculated as you intend, currently, your code is constraining the label height to the view's bound's height. Changing your maxSize instance to:
CGSize maxSize = CGSizeMake(self.view.bounds.size.width - 30, MAXFLOAT);
CGSize labelSize = ...
Doing so will ensure that the constraint is not bound by your view bounds. You may also want to consider setting the clipsToBounds property of your view if you want the label to be able to extend past your view's bounds.

Change font size via UiBarButtonItem in UITextView, works just once

I have UiTextView where I want change font size (increase), here code
-(void)biggerFont:(UIBarButtonItem *) item {
CGFloat i = [UIFont systemFontSize];
textView.font = [UIFont fontWithName:#"Tahome" size:i];
i+=2;
}
But it works just once, if you push once and after that again - fon size won't change. Help me please
It's easy you are not saving the state of i persistently. Everytime you set i to the same value. After the method has been run through i is discarded cause it only exists in the methods scope.
Change i to a property something like
CGFloat myFontSize;
#property(nonatomic) CGFloat myFontSize;
For example in the viewWillLoad you set the default value
self.myFontSize = [UIFont systemFontSize];
And your method changes to
-(void)biggerFont:(UIBarButtonItem *) item
{
myFontSize += 2;
textView.font = [UIFont fontWithName:#"Tahome" size:myFontSize];
}

heightForRowAtIndexPath not displaying multiline

heightForRowAtIndexPath is never displaying multiline when the text is longer than usual.
Here is my code:
#define FONT_SIZE 14.0f
#define CELL_CONTENT_WIDTH 320.0f
#define CELL_CONTENT_MARGIN 10.0f
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat result = 44.0f;
CGFloat width = 0;
CGFloat tableViewWidth;
CGRect bounds = [UIScreen mainScreen].bounds;
if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation))
tableViewWidth = bounds.size.width;
else
tableViewWidth = bounds.size.height;
width = tableViewWidth - 110; // fudge factor, 115 isn't quite right
NSUInteger index = [indexPath row];
id doc = [[self displayedObjects] objectAtIndex:index];
NSString *title = [doc title];
if (title)
{
// The notes can be of any height
// This needs to work for both portrait and landscape orientations.
// Calls to the table view to get the current cell and the rect for the
// current row are recursive and call back this method.
CGSize textSize = { width, 20000.0f }; // width and height of text area
CGSize size = [title sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:textSize lineBreakMode:UILineBreakModeWordWrap];
size.height += 29.0f; // top and bottom margin
result = MAX(size.height, 44.0f); // at least one row
}
return result;
}
Any help will be really appreciated.
Thanks!
To make label show multiline text you must set its numberOfLines property to a maximum allowed number of lines (or to 0 for arbitrary lines number). So in cellForRowAtIndexPath: method when setup your cell add:
...
cell.textLabel.numberOfLines = 0;
...
I suppose you don't use a custom UITableViewCell. So you're probably setting the title somewhere with something similar to the following code :
cell.textLabel.text = #"long long long text";
To get your text on multiple lines you have to configure the default title UILabel to display its text on multiple lines. Like this :
cell.textLabel.numberOfLines = 3;
You can compute the number of line the same way you compute the height of the cell. In fact I think you have to compute both to do what you want (height adapted to the content).
CGSize size = [title sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:textSize lineBreakMode:UILineBreakModeWordWrap];
What matters in textSize is to set the row width for the width attribute and an height value which is "large" enough to "host" the text.
Once you have your size initialized you can divide the height by the height of the UILabel line.