iOS, how to draw the dynamic length of text? - ios

The picture is: http://www.flickr.com/photos/71607441#N07/6641626163/
The background is a UIImageView, and the blue part I want to show as "title" of the image.
I used UILabel, but the length of the text is dynamic. It can be one line or two line, at most two line. If the text is longer than two lines, it will be truncated.
The blue part looks like "highlight in Microsoft Word", but it is not "highlight in iOS UILabel.text"
Is there anyone can help me?

Try this code :----
CGSize maximumSize = CGSizeMake(320, 30);
UILabel *newsLabel = [[UILabel alloc] init];
newsLabel.textColor = [UIColor whiteColor];
newsLabel.font = [UIFont boldSystemFontOfSize:11];
newsLabel.backgroundColor = [UIColor clearColor];
newsLabel.lineBreakMode = UILineBreakModeWordWrap;
newsLabel.numberOfLines = 0;
lineBreakMode:newsLabel.lineBreakMode];
CGSize dateStringSize = [#"Text Input" sizeWithFont:newsLabel.font
constrainedToSize:maximumSize
lineBreakMode:newsLabel.lineBreakMode];
CGRect dateFrame = CGRectMake(5, 5, 320, dateStringSize.height); //breath can be any desired float value
newsLabel.text = #"Text Input";
newsLabel.textAlignment = UITextAlignmentCenter;
newsLabel.frame = dateFrame;

You can find the size in pixels required for your title using:
CGSize size = [UILabel.text sizeWithFont:yourFont];
there's also:
CGSize size = [UILabel.text sizeWithFont:yourFont lineBreakMode: yourLineBreakMode];
You could then use these dimensions (size.width, size.height) to set the frame of your UILabel.
Hope this helps. :)

Related

Putting text in Circle as big as possible

It´s a pretty basic problem but I couldn´t find a proper solution for it. I have several circles which have text in it like you can see in the picture. The text gets loaded dynamically and has a size from one word up to five words or more. The goal is to put the text as big as possible into the circle. New lines can appear but every individual word should stay together. The example image is kind of ok but I would prefer the text to be bigger because there is still some free space between the text and the circle. The circle is 80x80. All solution I tried cropped the text strangly or the text is too small.
How I create the label:
UILabel *buttonlabel = [[UILabel alloc] initWithFrame:CGRectMake(12,7,57,64)];
[buttonlabel setText: #"Recipes"];
buttonlabel.font = [UIFont fontWithName:#"HelveticaNeue-Light" size:18.0f];
buttonlabel.textColor = [UIColor whiteColor];
buttonlabel.textAlignment = NSTextAlignmentCenter;
buttonlabel.lineBreakMode = NSLineBreakByWordWrapping;
buttonlabel.numberOfLines = 3;
[button addSubview:buttonlabel];
[buttonlabel release];
EDIT:
So I tried the solution of Rufel. I think the shrinking kind of works but my words get ripped apart. Even though I have buttonlabel.lineBreakMode = NSLineBreakByWordWrapping;
It looks like this:
This is my code. I also implemented the other methods mentioned in an answer.
//Create the button labels
UILabel *buttonlabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
[buttonlabel setText: #"text";
buttonlabel.textColor = [UIColor whiteColor];
buttonlabel.textAlignment = NSTextAlignmentCenter;
buttonlabel.lineBreakMode = NSLineBreakByWordWrapping;
buttonlabel.numberOfLines = 0;
CGFloat fontSize = 20; // The max font size you want to use
CGFloat labelHeightWithFont = 0;
UIFont *labelFont = nil;
do {
// Trying the current font size if it fits
labelFont = [UIFont systemFontOfSize:fontSize--];
CGRect boundingRect = [self boundingRectForString:subcatbuttontitlesarray[buttonTag-1] font:labelFont];
labelHeightWithFont = boundingRect.size.height;
// Loop until the text at the current size fits the maximum width/height.
} while (labelHeightWithFont > [self buttonLabelMaxWidth]);
buttonlabel.text = subcatbuttontitlesarray[buttonTag-1];
buttonlabel.font = labelFont;
- (CGRect)boundingRectForString:(NSString *)string font:(UIFont *)font
{
return [string boundingRectWithSize:CGSizeMake([self buttonLabelMaxWidth], MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
attributes:#{NSFontAttributeName: font}
context:nil];
}
- (CGFloat)buttonLabelMaxWidth
{
CGFloat hypotenuse = CGRectGetWidth(CGRectMake(0, 0, 60, 60));
CGFloat rightTriangleCathetus = sqrtf((hypotenuse*hypotenuse)/2);
return rightTriangleCathetus;
}
I found this thread here:
iOS7 - Adjusting font size of multiline label to fit its frame
which has the same problem.
Edit 2:
After searching a complete day for the solution and trying all kinds of combinations of the label attributes I somehow figured out that the "numberoflines" is my culprit. So I came up with this dumb solution of counting the words in the string and adjust the number of lines based on the numbers of the string:
NSString *samplestring = #"Three words string";
//Count the words in this string
int times = [[samplestring componentsSeparatedByString:#" "] count]-1;
UILabel *testlabel = [[UILabel alloc]initWithFrame:CGRectMake(30, 30, 60, 60)];
[testlabel setText:samplestring];
[testlabel setFont:[UIFont fontWithName:#"HelveticaNeue-UltraLight" size:40.0f]];
[testlabel setBackgroundColor:[UIColor redColor]];
[testlabel setAdjustsFontSizeToFitWidth:YES];
[testlabel setTextAlignment:NSTextAlignmentCenter];
//My workaround
if(times ==0){
[testlabel setNumberOfLines:1];
}else{
if(times==1){
[testlabel setNumberOfLines:2];
}
else{
[testlabel setNumberOfLines:3];
}}
[self.view addSubview:testlabel];
What you want to do, I think, is to ask the NSString for its boundingRectWithSize:options:attributes:context:. By setting the width of the bounding rect, you can find out what the height would be. You can use the parametric formula for the circle to determine whether that bounding rect fits entirely within the center of the circle. Unfortunately you will have to perform a kind of trial-and-error sequence of approximations, where the text gets larger and larger until the top and bottom stick out of the circle, and then narrow the proposed width and see whether this causes the height to grow too much because the text now wraps an extra time.
Say you have a custom view in which you draw a circle that fits its frame (80x80 in your example).
You will first want to find the maximum width your label can take without letters crossing the circle:
- (CGFloat)buttonLabelMaxWidth
{
CGFloat hypotenuse = CGRectGetWidth(self.bounds);
CGFloat rightTriangleCathetus = sqrtf((hypotenuse*hypotenuse)/2);
return floorf(rightTriangleCathetus);
}
Next, when you pass the title to display, you will want to iterate by decreasing an initially oversized font until the resulting string boundary fits the width previously calculated (which is also the maximum height since it's a circle). UPDATE: You will also want to check every words in the title to be sure they are not being truncated (that they fit the maximum width).
- (void)setButtonTitle:(NSString *)title
{
CGFloat fontSize = 20; // The max font size you want to use
CGFloat minimumFontSize = 5; // The min font size you want to use
CGFloat labelHeightWithFont = 0;
CGFloat longestWordWidth = 0;
UIFont *labelFont = nil;
CGFloat buttonLabelMaxWidth = [self buttonLabelMaxWidth];
do {
if (fontSize < minimumFontSize) {
// Handle exception where the title just won't fit
break;
}
// Trying the current font size if it fits
labelFont = [UIFont systemFontOfSize:fontSize--];
CGSize boundingSize = [self boundingSizeForString:title font:labelFont];
labelHeightWithFont = boundingSize.height;
// Be sure that words are not truncated (that they fits in the maximum width)
longestWordWidth = 0;
for (NSString *word in [title componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]) {
CGSize wordSize = [word sizeWithAttributes:#{NSFontAttributeName: labelFont}];
longestWordWidth = MAX(longestWordWidth, wordSize.width);
}
// Loop until the text at the current size fits the maximum width/height.
} while (labelHeightWithFont > buttonLabelMaxWidth || longestWordWidth > buttonLabelMaxWidth);
self.buttonLabel.text = title;
self.buttonLabel.font = labelFont;
}
- (CGSize)boundingSizeForString:(NSString *)string font:(UIFont *)font
{
CGRect boundingRect = [string boundingRectWithSize:CGSizeMake([self buttonLabelMaxWidth], MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
attributes:#{NSFontAttributeName: font}
context:nil];
return CGSizeMake(ceilf(boundingRect.size.width), ceilf(boundingRect.size.height));
}

Centered Single Character in UILabel off by 1 pixel

I am setting up the following UILabel:
UIImageView *textImageBackground = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"top100bar"]];
textImageBackground.frame = CGRectMake( 104.0f, 6.0f, 215.0f, 104.0f);
[self.contentView addSubview:textImageBackground];
_lblRank = [[UILabel alloc] init];
self.lblRank.textColor = [UIColor whiteColor];
self.lblRank.textAlignment = NSTextAlignmentCenter;
self.lblRank.font = [UIFont fontWithName:#"HelveticaNeue-Medium" size:10];
self.lblRank.frame = CGRectMake(153.0f, 45.0f, 41.0f, 41.0f);
self.lblRank.backgroundColor = [UIColor colorWithRed:.09 green:.62 blue:.11 alpha:1.0];
[textImageBackground addSubview:_lblRank];
If the text of _lblRank is set to a string 2 or more characters long, it centers the text perfectly, with an equal amount of pixels to the left and right of the text. However, if the string only has a single character, the text favors the left by 1 or 2 pixels. I measured this by grabbing a screenshot of the simulator and zooming and measuring in Preview.
I have attached screenshots of single and multi character versions.
Thanks so much!
Stephen
First size to fit the label:
[self.lblRank sizeToFit]
Then center it horizontally:
CGRect frame = self.lblRank.frame
frame.origin.x = self.lblRank.superview.frame.size.width/2.0 - frame.size.width/2.0;
self.lblRank.frame = frame
Make sure the label has a superview before you use the code above.

How to add NSString to view in define size rectangle

I need to add a sentence into my view. But the sentence is quite big. So i would like to put it inside a defined CGRect, with several lines, and change the font size, that all sentence will be visible in this CGRect. And the font size should be as big as it is possible.
Here the code that i am using:
NSAttributedString *sentence = [[NSAttributedString alloc] initWithString:#"The sentence with some words" attributes:#{NSFontAttributeName: wordsFont,NSBackgroundColorAttributeName:[UIColor yellowColor]}];
CGRect sentenceBounds;
sentenceBounds.size = [sentence size];
CGSize neededSize = CGSizeMake(MAX_WIDTH, MAX_HAIGHT);
sentenceBounds.size = neededSize;
sentenceBounds.origin = CGPointMake(0, 0);
[sentence drawInRect:sentenceBounds];
All of the functionality you're looking for is included with UILabel. Is there a reason you can't just use that?
UILabel *label = [UILabel new];
label.adjustsFontSizeToFitWidth = YES;
label.numberOfLines = 0;
label.attributedText = sentence;
label.frame = CGRectZero; //set desired frame here;
[self addSubview:label];

Having difficulty wrapping text

I use the following code to add text in IOS
//Set up label frame
UILabel *tempLabel = [[UILabel alloc]initWithFrame:CGRectMake(100, 10, 210, 80)];
self.answer_text_label = tempLabel;
[tempLabel release];
[self.preview_answer_container addSubview:self.answer_text_label];
//Populate label with text
self.answer_text_label.text = self.answer.text;
self.answer_text_label.numberOfLines = 4;
self.answer_text_label.lineBreakMode = UILineBreakModeWordWrap;
[self.answer_text_label sizeToFit];
However, the result I get is as such, the text seems to overflow to the right instead of staying within the frame as stipulated in my label setup 'CGRectMake(100, 10, 210, 80)'
The wrapping works if I change to self.answer_text_label.numberOfLines = 0. But this will not work for me since I need to constrain the text within my stipulated label frame.
Any way I can wrap the text and keep to only 4 lines?
EDIT:
Try the suggested code
self.answer_text_label.text = self.answer.text;
[self.answer_text_label sizeToFit];
CGRect labelRect = self.answer_text_label.frame;
labelRect.origin.y = CGRectGetMaxY(labelRect);
labelRect.size = self.answer_text_label.frame.size;
self.answer_text_label.frame = labelRect;
result as follows. Did not seem to solve my problem
Try setting frame explicitly -
[self.answer_text_label sizeToFit];
CGRect labelRect = self.answer_text_label.frame;
labelRect.origin.y = CGRectGetMaxY(labelRect);
labelRect.size = self.answer_text_label.frame.size;
self.answer_text_label.frame = labelRect;
EDIT - Don't need to use this, just use following -
remove these of code just use below, no other property of frame, remove sizeToFit as well -
self.answer_text_label.numberOfLines = 4;
self.answer_text_label.lineBreakMode = UILineBreakModeWordWrap;
For vertical alignment - (With above line of code, use this as well, and do don't use size to fit)
CGSize textSize = [self.answer_text_label.text sizeWithFont:self.answer_text_label.font
constrainedToSize:CGSizeMake(self.answer_text_label.frame.size.width, MAXFLOAT)
lineBreakMode:self.answer_text_label.lineBreakMode];
self.answer_text_label.frame = CGRectMake(20.0f, 20.0f, textSize.width, textSize.height);
In iOS 6 and later, use NSLineBreakByWordWrapping, not UILineBreakModeWordWrap.
self.answer_text_label.lineBreakMode = NSLineBreakByWordWrapping;

How to calculate UILabel width based on text length?

I want to display an image next to a UILabel, however UILabel has variable text length, so I don't know where to place the image. How can I accomplish this?
CGSize expectedLabelSize = [yourString sizeWithFont:yourLabel.font
constrainedToSize:maximumLabelSize
lineBreakMode:yourLabel.lineBreakMode];
What is -[NSString sizeWithFont:forWidth:lineBreakMode:] good for?
this question might have your answer, it worked for me.
For 2014, I edited in this new version, based on the ultra-handy comment by Norbert below! This does everything.
// yourLabel is your UILabel.
float widthIs =
[self.yourLabel.text
boundingRectWithSize:self.yourLabel.frame.size
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{ NSFontAttributeName:self.yourLabel.font }
context:nil]
.size.width;
NSLog(#"the width of yourLabel is %f", widthIs);
yourLabel.intrinsicContentSize.width for Objective-C / Swift
In swift
yourLabel.intrinsicContentSize().width
The selected answer is correct for iOS 6 and below.
In iOS 7, sizeWithFont:constrainedToSize:lineBreakMode: has been deprecated. It is now recommended you use boundingRectWithSize:options:attributes:context:.
CGRect expectedLabelSize = [yourString boundingRectWithSize:sizeOfRect
options:<NSStringDrawingOptions>
attributes:#{
NSFontAttributeName: yourString.font
AnyOtherAttributes: valuesForAttributes
}
context:(NSStringDrawingContext *)];
Note that the return value is a CGRect not a CGSize. Hopefully that'll be of some assistance to people using it in iOS 7.
Swift 4 Answer who are using Constraint
label.text = "Hello World"
var rect: CGRect = label.frame //get frame of label
rect.size = (label.text?.size(attributes: [NSFontAttributeName: UIFont(name: label.font.fontName , size: label.font.pointSize)!]))! //Calculate as per label font
labelWidth.constant = rect.width // set width to Constraint outlet
Swift 5 Answer who are using Constraint
label.text = "Hello World"
var rect: CGRect = label.frame //get frame of label
rect.size = (label.text?.size(withAttributes: [NSAttributedString.Key.font: UIFont(name: label.font.fontName , size: label.font.pointSize)!]))! //Calculate as per label font
labelWidth.constant = rect.width // set width to Constraint outlet
In iOS8 sizeWithFont has been deprecated, please refer to
CGSize yourLabelSize = [yourLabel.text sizeWithAttributes:#{NSFontAttributeName : [UIFont fontWithName:yourLabel.font size:yourLabel.fontSize]}];
You can add all the attributes you want in sizeWithAttributes.
Other attributes you can set:
- NSForegroundColorAttributeName
- NSParagraphStyleAttributeName
- NSBackgroundColorAttributeName
- NSShadowAttributeName
and so on. But probably you won't need the others
CGRect rect = label.frame;
rect.size = [label.text sizeWithAttributes:#{NSFontAttributeName : [UIFont fontWithName:label.font.fontName size:label.font.pointSize]}];
label.frame = rect;
Here's something I came up with after applying a few principles other SO posts, including Aaron's link:
AnnotationPin *myAnnotation = (AnnotationPin *)annotation;
self = [super initWithAnnotation:myAnnotation reuseIdentifier:reuseIdentifier];
self.backgroundColor = [UIColor greenColor];
self.frame = CGRectMake(0,0,30,30);
imageView = [[UIImageView alloc] initWithImage:myAnnotation.THEIMAGE];
imageView.frame = CGRectMake(3,3,20,20);
imageView.layer.masksToBounds = NO;
[self addSubview:imageView];
[imageView release];
CGSize titleSize = [myAnnotation.THETEXT sizeWithFont:[UIFont systemFontOfSize:12]];
CGRect newFrame = self.frame;
newFrame.size.height = titleSize.height + 12;
newFrame.size.width = titleSize.width + 32;
self.frame = newFrame;
self.layer.borderColor = [UIColor colorWithRed:0 green:.3 blue:0 alpha:1.0f].CGColor;
self.layer.borderWidth = 3.0;
UILabel *infoLabel = [[UILabel alloc] initWithFrame:CGRectMake(26,5,newFrame.size.width-32,newFrame.size.height-12)];
infoLabel.text = myAnnotation.title;
infoLabel.backgroundColor = [UIColor clearColor];
infoLabel.textColor = [UIColor blackColor];
infoLabel.textAlignment = UITextAlignmentCenter;
infoLabel.font = [UIFont systemFontOfSize:12];
[self addSubview:infoLabel];
[infoLabel release];
In this example, I'm adding a custom pin to a MKAnnotation class that resizes a UILabel according to the text size. It also adds an image on the left side of the view, so you see some of the code managing the proper spacing to handle the image and padding.
The key is to use CGSize titleSize = [myAnnotation.THETEXT sizeWithFont:[UIFont systemFontOfSize:12]]; and then redefine the view's dimensions. You can apply this logic to any view.
Although Aaron's answer works for some, it didn't work for me. This is a far more detailed explanation that you should try immediately before going anywhere else if you want a more dynamic view with an image and resizable UILabel. I already did all the work for you!!

Resources