Aligning floats with two decimals by the point [duplicate] - ios

This question already has an answer here:
Decimal point alignment in iOS UILabel during countdown?
(1 answer)
Closed 5 years ago.
I have a bunch of UILabels inside a vertical UIStackView. The stack is making all labels with the same width and height.
Each label has a float in it, like this:
But this is ugly aesthetically. I want to align the numbers by the point. Like this:
One of my problem is that these floats are in different labels.
These numbers use this to format:
[NSString stringWithFormat:#"%.2f", value];
I don't have a clue if even it is possible. So, I have no code to show. Is it possible to do that? How?
EDIT: The floats are left aligned on the labels. Ok, I can reduce the labels width and align them to the right, but I am wondering if there is another solution in code because reducing the labels's width will cause other problems on the interface, crapping the whole thing.

If, as your edit says, you don't want to set your labels' textAlignment to right (which is certainly the easiest solution), here's another solution. You need to do two things:
You need to use a font with monospaced digits. In iOS 9 and later, the system font does not use monospaced digits by default, but there are programmatic ways to get a variant of the system font that does use monospaced digits. You can use +[UIFont monospacedDigitSystemFontOfSize:weight:], or if you already have a UIFont object, you can get a monospaced-digits variant (if there is one) by adding an attribute to its font descriptor. See this answer for code to do this (in Swift). Unfortunately there is no way to do this in a storyboard or xib without code.
You need to add enough U+2007 FIGURE SPACEs to the beginning of each label's text to make them all the same length. For example, if they should all be five characters long:
NSString *text = [NSString stringWithFormat:"%.2f", value];
while (text.length < 5) {
text = [#"\u2007" stringByAppendingString:text];
}
label.text = text;

You can try to set the UILabel to right align, e.g. Something like:
label.textAlignment = NSTextAlignmentRight;
Or for the single digit before the point case you could prepend the Unicode FIGURE SPACE, U+2007, this is a space with the same width as a digit in fonts with equal width digits and so all numbers will be the same width.

If you really need to do that from code without reducing UILabel's width, you can subclass UILabel and override this method:
- (void)drawTextInRect:(CGRect)rect {
UIEdgeInsets insets = {0, 0, 0, rightOffset};
[super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
}
Of course you should use right alignment as well.

Related

How can I add the three dots of a UIlabel when the text is too long, at the middle of the text instead of at the end?

For example, I have the following two string: "How Munched is That Birdie in the Window?" and "S22 - E7". I want to present in the label the following: "How Munched is That Birdie in ... S22 - E7" If the string is too large according to the label's size and doesn't fit it". How you can see, the three dots are placed always in the first string, the second string is always shown full.
How can I achieve this?
Here is how to do it.
yourlabel.adjustsFontSizeToFitWidth = false
yourlabel.lineBreakMode = .byTruncatingMiddle
You can set the UILabel's ParagraphStyle LineBreakMode to byTruncatingMiddle, which will probably work in most cases with carefully planned label size. From the docs:
The line is displayed so that the beginning and end fit in the container and the missing text in the middle is indicated by an ellipsis glyph. This mode is used for single-line layout
If you want to guarantee none of the "S22 - E7" string is truncated, you would have more control using two labels and setting layout constraints such that the width of the protected label is preserved so that it can display the full string whenever possible, but that is probably overkill in most cases.
You may also find this answer helpful if decide to go a different route by manually manipulating the displayed string based on detecting how many characters will be visible given the width and font.

Auto size UILabel text

I have a UILabel with multi lines. I'm trying to get the text to auto size, and fit on the line it's in. So instead of:
longe
st
The text size should become smaller so that it can fit on the same line:
longest
Note: I only need it for the first line.
I tried the following:
label.adjustsFontSizeToFitWidth = YES;
label.minimumScaleFactor=3.0;
That didn't work, so I tried label.minimumScaleFactor=3.0 / 9; (9 is the font size.) That also didn't work.
I have a UILabel with multi lines
Well, that's the problem. The automatic size adjustment feature of UILabel works only for 1-line UILabels (i.e. numberOfLines must be 1).
You might be happier with two labels, one being a single-line UILabel for the first line which can shrink its size, the other for the remaining lines.

Ignore Ascender and Descender when centering UILabel vertically?

I’m using AutoLayout to position some labels in the vertical centre of a cell. The text is in all-caps, but the UILabel in question, even when sizeToFit is applied, leaves space below the text, which looks a lot like it would be for the tails on letters such as a lower case y, p, and q. Since I’m centring vertically, this is causing an offset and meaning the text appears a few pixels higher than it should do.
Another question may be: can I have a font intelligently adjust its vertical centre dependant on whether it contains any characters which use the ascender or descender?
For instance, the string “abbaba” doesn’t need the descender, whereas the string “oyyoyo” doesn’t need the ascender. Strings in all-caps also never need the descender. If I vertically center “oyyoyoyo” it’ll appear too low.
Thanks, Abhinit, for your answer.
I was also looking for this so I would like to post here the exact constraints you need to apply to align texts to your liking.
This image from Wikipedia shows the different size sections of a font.
So, there are many ways to align a label depending on whether you want to align to the ascender height, the cap height, the x-height, baseline or descender height.
Let's say you have a label containing text in caps like "HELLO" and you want to align with viewAbove to cap height and align with viewBelow to baseline.
You would do:
let font = label.font
let ascenderDelta = font.ascender - font.capHeight
LayoutHelper()
.addViews([
"label":label, "viewAbove":viewAbove, "viewBelow":viewBelow
])
.withMetrics(["ascenderDelta":ascenderDelta])
.addConstraints([
// -- Here the constraints to align to cap height --
"X:label.top == viewAbove.bottom - ascenderDelta",
"X:label.baseline == viewBelow.top",
...other constraints...
])
Note: in the example I'm using my utility class LayoutHelper, but I hope the idea is clear.
About an "auto-aligning" label:
I will think about making an "intelligent" label that adjusts to the appropriate line depending on whether it contains descenders, ascenders, caps, etc.
You could do it using negative insets in drawTextInRect(like here but, for example, using insets = {-ascenderDelta, 0, font.descender, 0}). But that would crop any ascenders/descenders in case you had. I would prefer to align to caps without cropping any possible ascender.
You can use the 'capHeight' and 'xHeight' properties on UIFont to get the correct height and use that to size the UILabel.
Of course this assumes that you know for sure if a string would be lowercase or uppercase only. If not, then you can override setText on a UILabel and check every time the function gets called.
I would also think of looking deeper into CoreText and implementing something like this http://www.zsiegel.com/2012/10/23/Core-Text-Calculating-line-heights/
I had the same problem and solved it by subclassing UILabel and changing its draw method:
- (void)drawRect:(CGRect)rect
{
CGRect capCenteredRect = CGRectMake(rect.origin.x, (self.font.leading-self.font.capHeight)*0.5f, rect.size.width, rect.size.height);
[super drawTextInRect:capCenteredRect];
}
In my case I needed to center to caps because the vertical centering of the UILabel is always some pixels off.
If you need to center vertically to lower case letters with descender you can change the rect to:
CGRectMake(rect.origin.x, (self.font.leading-self.font.xHeight)*0.5f+self.font.descender, rect.size.width, rect.size.height)

How to justify text perfectly on iOS 7

I'm trying to horizontally align text bits with different size and fonts.
Here is the result I'd like (made with scribus):
You can see that text is horizontally align perfectly (to the pixel).
I tried implementing this with a basic UITextView, and justifying the text. Here is what I got:
You can see that the text is not horizontally aligned to the pixel.
I tried centering and it's still not good enough:
If things were not hard enough, the first and third line are dynamic text... so their size changes.
I had a look at textkit but didn't find anything conclusive...
My leads are:
find best font size for text given the width.
space glyphs evenly so that the text bounding box is equal to the desired width.
but I have no clue how to do that.
Any leads ? thanks !
Have you tried NSString's boundingRectWithSize:options:attributes:context:?
CGRect rect = [string boundingRectWithSize:CGSizeMake(width, height)
options:NSStringDrawingUsesFontLeading
attributes:attributes
context:nil];
If you are also supporting iOS 6, use -[NSString sizeWithFont:constrainedToSize:lineBreakMode:]
CGSize textSize = [string sizeWithFont:uifont
constrainedToSize:maximumCGSize
lineBreakMode:lineBreakMode];
The sizeWithFont:constrainedToSize:lineBreakMode: method has been deprecated as of iOS 7.
Using one of these methods, you can find the line that is the widest and match the others to it. In your case of dynamic text, I would suggest against using UITextView to ensure text is correctly justified.

Calculate characters that fit a fixed rect TextView

I have seen here people needing to calculate the size of the NSString given a size but I need to do the opposite.
Given a specified rect (or fixed UITextView, or multiline UILabel, no scrolling) I need to know:
if it managed to show all the chars of my NSString
if not, what is the last char shown
So that I can display the remaining text in another UITextView (of course if I could use a single UITextView I would not have this problem).
At first it seems a simple thing to do, but actually I am not finding a way, intuitively I think I could use either UITextView's:
textView.contentSize.height;
or NSString's:
sizeWithFont:constrainedToSize:lineBreakMode:
or a combination of the two, but I need to be precise and those methods do not help me in telling what is the last character that managed to fit the visible area of the UITextView.
Not sure if this is actually possible, but is a requirement of my client who thinks programming iOS is like printing a newspaper and expects to be able to format text around an image....
You could maybe get the maximum height of one line of text from a one character long string.
If you use that with sizeWithFont:constrainedToSize:lineBreakMode: then you should be able to know if your text runs onto more than one line (if the cgsize height is greater than the height of one line of text).
In order to find out the last character (or word) you would have to loop around the length of the string adding characters (or words) as you go and checking for when the cgsize height increases to add a new line, this will give you the character point when to split into multiple strings ( for multiple fields/labels/textviews ) or when to insert line breaks into the text ( if using a single multi-line textview or label ).
I hope you find an easier way...

Resources