iOS - Get the "real" height of a letter - ios

I am trying to layout text on a UIView.
(The yellow area is the frame of the UILabel with a background color).
When I use sizeWithFont I get this, which has a very large space above the letter:
When I use font.pointSize i get this for "i" which is good-
BUT
When i use it for "p" I get the precise height but the letter is drawn in the bottom and cropped.
**How can i get get the glyph only centered in the frame ? **
Thanks
Shani

There are a lot of properties on UIFont to help in this situation:
pointSize
ascender
descender
capHeight
xHeight
lineHeight

You could convert the UILabel to a UIImage with a "printscreen" sort of function and then check the the pixels one by one (with for instance: How to get pixel data from a UIImage (Cocoa Touch) or CGImage (Core Graphics)?) and 'calculate' the left top en right bottom.

Try moving the text upwards by font.ascender - font.capHeight. Shrinking the height of a UILabel will likely clip its contents, so it is better to adjust the label's y position instead of resizing.
The following code sample explains the computation I used:
// in UILabel subclass:
- (CGFloat) topPadding
{
// ascender = height from baseline to top of label (including top padding)
// capHeight = height of a capital letter = ascender - top padding
// -> top padding = ascender - capHeight
return self.font.ascender - self.font.capHeight;
}

Related

UILabel with background view + padding that follows text layout

I am trying to achieve a pretty complicated layout in my UICollectionViewCells where I have a UILabel that should have a white background with padding + 2px corner radius, that should follow the text.
If the text is too long it should be two lines, and the background should contain its padding. It's pretty hard to explain, but I think the picture will tell more.
Let me know if you have any solutions or suggestions.
Put the label inside uiview and give it a white background and play with its constraints and background colour.
You could proceed as follow :
Check the size of your string
Compare it with the size of your box
If the text size is bigger than the size of the box, you increase its size
//Get your textfield size
var frameRect = textField.frame;
let myString: NSString = textField.text! as NSString
//Get the size of your string
let size: CGSize = myString.sizeWithAttributes([NSFontAttributeName: UIFont.systemFontOfSize(14.0)])
//If your text doesn't fit, increase the size of your box
if size.width > frameRect.width {
frameRect.size.height = 50; // <-- Specify the height you want here.
textField.frame = frameRect;
}
Tell me if this works

Make background UILabel bigger

I have a question in my app I have a number label (IBOutlet). And when I write self.numberLabel.backgroundColor = [UIColor redColor]; is showing a red color fit in the height of number, All I want is to make the background a little bit bigger. Thanks
self.numberLabel.frame = CGRectMake(self.numberLabel.frame.origin.x, self.numberLabel.frame.origin.y, widht, newHeight);
and to make sure font stay same size if needed
[self.numberLabel setFont:[UIFont systemFontOfSize:35]];
You have to set constraints in Interface Builder / Storyboard to fix your label height/width.
If your label's content changes in width, you can use this to calculate a new width with a bit of space left:
float labelWidth =
[self.myLabel.text
boundingRectWithSize:self.myLabel.frame.size
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{ NSFontAttributeName:self.myLabel.font }
context:nil]
.size.width;
CGRect rect = self.myLabel.bounds;
rect.size.width = labelWidth + 15.0f; //replace 15.0F with the value you want
[self.myLabel setBounds:rect];
There're many ways to skin a cat... in my case the content of the "uilabel" determines its size. I just want the background to be slightly bigger -- 5 points vertically, 7 points horizontally. So I use autolayout to solve it.
Add a uilabel as a subview to the background which is a uiview
Add constraints in IB between the uilabel and the uiview
Add constraints in IB between the uiview and its superview

Stretch width of UILabel according to width of containing text with background image

I have the following problem - I need to create two UI labels along side one another as in the screen shot below -
The UI label containing the special offers text is dynamic and needs to adjust to the width of the containing text and also if possible display the slanted orange background with the relevant padding -
I'm predominantly a Front-end dev - so with CSS i'd use a long background image that aligns to the right of the label and pad accordingly - but I have no idea how to approach this in objective C - can anyone offer any advice?
This is not a drop-in solution, but perhaps helps you find it (assuming you don't use Auto Layout):
You need a UIImageView for the background and a UILabel for the text
Use a tileable/strechable image (probably with cap insets) for the background (see [UIImage resizableImageWithCapInsets:resizingMode:])
Set the text on your label
Call [label sizeToFit] to resize the label to exactly fit the contained text
Resize the image view depending on the label's size (e.g. imageView.frame = CGRectInset(label.frame, -10, -10), which would make your image view 10pt larger than the label on all sides).
With Auto Layout you'd just define the appropriate constraints between the label and the image view and rely on the label's "intrinsic content size" - should be quite easy.
You could always shrink the text when it gets to long for the label, go into your view controller, click on the label that you would like to shrink when the text gets too long, then, go down to Autoshrink under Label in the attributes inspector. Change it from fixed font size to minimum font size, then I recommend putting 6 to 8 as the lowest font size. This is going to be the LOWEST font size though, so if XCode can make the label fit while making the font size 9, yet the lowest is 7, it will do it.
Or, you could get the length of the string with
int *stringLength = [myString length]
which counts spaces too, then, change the orange square with
orangeBoxImageView.frame = CGRectMake(0, 0, resizedWidth, resizedHeight);
so you could do something like this with your code:
if(stringLength == 15{
orangeBoxImageView.frame = CGRectMake(0, 0, 15, 5);
}
I hope this could help you
CGFloat rect = [YOURSTRING
boundingRectWithSize: (CGSize){ labelWidth, CGFLOAT_MAX }
options: NSStringDrawingUsesLineFragmentOrigin
attributes: #{ NSFontAttributeName : labelFont };
context: nil];
This assumes numberOfLines = 0.
What you want to use is UILabel -sizeToFit inherited from UIView. You should be able to figure out the rest from there.
Find the size of the containing text with
[text sizeWithFont:font constrainedToSize:CGSizeMake(width, height) lineBreakMode:NSLineBreakModexx]
Once you have that, you can manipulate the size of the label accordingly.
Hope this helps
For the background I'd create a subclass of UILabel that overrides drawInRect:, such as this example that simply draws a rectangle with the size of the label
-(void) drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(c, [UIColor redColor].CGColor);
CGContextFillRect(context, rect);
}
Rather than drawing a rectangle, you should create a path with the shape that you need and fill that. See here for more info:
http://weblog.invasivecode.com/core-graphics
I don't know how you've created your label, but if it's done properly with autolayout it'll have the correct width automagically. See here:
http://www.raywenderlich.com/20881/
Tim

Positioning a UILabel directly beneath another UILabel

I have an addition to NSString which automatically resizes a UILabel depending on the text that's being read into it (I have a simple app showing quotations, so some are a few words, some a couple sentences). Below that quote label, I also have an author label, which (oddly enough) has the author of the quote in it.
I'm trying to position that author label directly beneath the quote label (as in, its y coordinate would be the quote label's y coordinate plus the quote label's height. What I'm seeing is some space being placed between the two labels, that depending on the length of the quote, changes size. Smaller quotes have more space, while longer quotes have less space. Here's a quick diagram of what I'm seeing:
Note the gap between the red and blue boxes (which I've set up using layer.borderColor/borderWidth so I can see them in the app), is larger the shorter the quote is.
If anyone can sift through the code below and help point me towards exactly what's causing the discrepancy, I'd be really grateful. From what I can see, the author label should always be 35 pixels beneath the quote label's y + height value.
Just to confirm: everything is hooked up correctly in Interface Builder, etc. The content of the quote's getting in there fine, everything else works, so it's hooked up, that isn't the issue.
To clarify, my question is: Why is the gap between the labels changing dependant on the quote's length, and how can I get a stable, settable gap of 35 pixels correctly?
Here's the code I'm using to position the labels:
// Fill and format Quote Details
_quoteLabel.text = [NSString stringWithFormat:#"\"%#\"", _selectedQuote.quote];
_authorLabel.text = _selectedQuote.author;
[_quoteLabel setFont: [UIFont fontWithName: kScriptFont size: 28.0f]];
[_authorLabel setFont: [UIFont fontWithName: kScriptFontAuthor size: 30.0f]];
// Automatically resize the label, then center it again.
[_quoteLabel sizeToFitMultipleLines];
[_quoteLabel setFrame: CGRectMake(11, 11, 298, _quoteLabel.frame.size.height)];
// Position the author label below the quote label, however high it is.
[_authorLabel setFrame: CGRectMake(11, 11 + _quoteLabel.frame.size.height + 35, _authorLabel.frame.size.width, _authorLabel.frame.size.height)];
Here's my custom method for sizeToFitMultipleLines:
- (void) sizeToFitMultipleLines
{
if (self.adjustsFontSizeToFitWidth) {
CGFloat adjustedFontSize = [self.text fontSizeWithFont: self.font constrainedToSize: self.frame.size minimumFontSize: self.minimumScaleFactor];
self.font = [self.font fontWithSize: adjustedFontSize];
}
[self sizeToFit];
}
And here's my fontSizeWithFont:constrainedToSize:minimumFontSize: method:
- (CGFloat) fontSizeWithFont: (UIFont *) font constrainedToSize: (CGSize) size minimumFontSize: (CGFloat) minimumFontSize
{
CGFloat fontSize = [font pointSize];
CGFloat height = [self sizeWithFont: font constrainedToSize: CGSizeMake(size.width, FLT_MAX) lineBreakMode: NSLineBreakByWordWrapping].height;
UIFont *newFont = font;
// Reduce font size while too large, break if no height (empty string)
while (height > size.height && height != 0 && fontSize > minimumFontSize) {
fontSize--;
newFont = [UIFont fontWithName: font.fontName size: fontSize];
height = [self sizeWithFont: newFont constrainedToSize: CGSizeMake(size.width, FLT_MAX) lineBreakMode: NSLineBreakByWordWrapping].height;
};
// Loop through words in string and resize to fit
for (NSString *word in [self componentsSeparatedByCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]) {
CGFloat width = [word sizeWithFont: newFont].width;
while (width > size.width && width != 0 && fontSize > minimumFontSize) {
fontSize--;
newFont = [UIFont fontWithName: font.fontName size: fontSize];
width = [word sizeWithFont: newFont].width;
}
}
return fontSize;
}
After you called size to fit on both labels, calculate the distance between their frames and change them accordingly:
[quoteLabel sizeToFit];
[authorLabel sizeToFit];
float distance = authorLabel.frame.origin.y - quoteLabel.frame.size.height;
float difference = distance - 35;
authorLabel.frame = CGRectMake(authorLabel.frame.origin.x,(authorLabel.frame.origin.y - difference),authorLabel.frame.size.width,authorLabel.frame.size.height);
The reason the gap changes is that the quote label frame changes its height dependent on its content when you call sizeToFit.
UPDATE
Given the recent developments in the comments, I think you have 3 possibilities:
resize the whitespace instead of only the words, so that the string
actually fits in the frame correctly
somehow access the CTFramesetter of UILabel to see what the actual
frame, when all is said and done, amounts to
make your own UIView subclass that handles Core Text drawing in its
draw rect method (should be easy in your case), since after all you
are trying to give to UILabel a behavior that it's not meant for
It probably is moving where you want it, but then an auto-layout constraint or a spring/strut is moving it afterwards.
EDIT:
My first thought (which I ruled out because you said that the box around the words was the label frame. In later comments, you say that this is not an actual screen shot, but just a representation of it, so it could still be correct) was that you are doing this wrong:
[_quoteLabel sizeToFitMultipleLines];
[_quoteLabel setFrame: CGRectMake(11, 11, 298, _quoteLabel.frame.size.height)];
In the first line, you are sizing the text to fit in whatever the current width of the label might be, and then you turn around in the second line and change the width of the label. So most likely, what is happening is that you are sizing the label for some smaller width, which makes it tall. You then make the label wider than it was before and the text expands to fit the wider label, leaving a blank area beneath the actual text, although the frame has not changed. This makes the space betwee the labels exactly 35 as you want, however the top label's text does not go all of the way to the bottom of its frame so the white space is more than you want. Basically, you have this:
*************
* text text *
* text text *
* *
* *
* *
*************
*************
* text text *
*************
If this is the case, then you would fix it by setting the width first, like this:
// You could put anything for the 200 height since you will be changing it in the next line anyway.
[_quoteLabel setFrame: CGRectMake(11, 11, 298, 200];
[_quoteLabel sizeToFitMultipleLines];
I ended up solving the problem by using a single UILabel, and CoreText with an NSAttributedString. Kind of a cop-out, but it works.

UILabel with constant size and dynamic font size depends on text

I have UILabel on my UIView. I always get various data for my label(from backend). I have some rect to display text on View (i set it as uilabel size, this size is constant).
How to set dynamic font for my UILabel for its size?
I just need to display text data with actual font size, for my rect bounds. If text really long, and minimal font will wrap text - i need to apply minimal font size to display more text data.
(my label can be multi lines to be sized to const rect bounds)
There is property in UILable - adjustToFitWidth in xib, where you can give minimum font size. So if your text is getting longer then it will automatically reduce font size to adjust it within the label.
Note: whatever value given in adjustToFitWidth, is minimum limit, so your font size always be greater or equals to that value.
Enjoy Coding :)
I would do this in this way: Get the NSString character count. Based on that character count, calculate the size.
NSString *myString = #"Hello";
int myInt = myString.length;
float fontSize = PUT_A_VALUE_HERE / myInt;
You are going to play with the variable PUT_A_VALUE_HERE until you get desired results. After that, change the font size
[lblYourUILabel setFont:[UIFont fontWithName:fontName size:fontSize]];

Resources