"Best fit" text option to shrink only - unity3d

Is there an easy way to dynamically resize a text to fit its contents, not exceeding certain maximum size? I mean, something like one-way "best fit" option, one that shrinks only.
I tried to manipulate the size of the best-fit text by script, but that doesn't do anything:
private void ModifyText(Text text, string str, int maxSize)
{
text.text = str;
if (text.fontSize > maxSize) text.fontSize = maxSize;
}

Best fit lets you define minimum and maximum font sizes, right in the inspector

Related

UILabel splitting in the middle of words (or how to get proper word wrapping)

I'm trying to fit some text into a fixed-width label (actually the width depends on the screen size, but cannot change within the app), and expect UIKit to cleverly use a combination of font resizing and word-wrapping to get the proper result. However, it doesn't seem to work that way. Considering a UILabel with the following constraints:
aspect ratio = 1:1
label.width = 0.7 * parentView.width (all other relevant constraints set, no errors or warnings in IB)
and the following code:
label.font = label.font.withSize(100)
label.adjustsFontSizeToFitWidth = true
label.lineBreakMode = .byClipping
label.numberOfLines = 0
label.text = "Shooter team"
I would be hoping that it would resize the text and make it fit into two lines: "Shooter" and "team" (or, since the text could be anything, split it properly into words). However, when I set label.lineBreakMode to .byWordWrapping, it doesn't resize the text at all and so only one big letter is displayed (note: I'm using a big font size for it to resize because I can't know in advance how big the text is going to be, since the size depends on the screen size). Any other value for .lineBreakMode results in the text being resized but split into "Shoote" and "r team", which looks dumb. Changing autoshrink to e.g. Minimum font size = 8 doesn't seem to have any effect. See screenshot below.
Any suggestion of how I can get the proper splitting/resizing? I may have used the wrong terms for my searches but I haven't found any answer :-|
(Note: there will be a different question about how I can get the border of the encompassing view to be a nice circle prior to the view being displayed :-| )
First, to take advantage of adjustsFontSizeToFitWidth, you must also give it a scale factor (the smallest size you're willing to let the label shrink to). So if, for example, your label's font is sized at 30, you could let it shrink down to 24:
someLabel.font = UIFont(name: "someFont", size: 30)
someLabel.adjustsFontSizeToFitWidth = true
someLabel.minimumScaleFactor = (24/30)
Second, you may want to consider using an attributed title for your label to take advantage of paragraph styling. Paragraph styling lets you play with hyphenation rules:
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.hyphenationFactor = 1.0
let attributedTitle = NSAttributedString(string: "Shooter team", attributes: [NSAttributedString.Key.foregroundColor: UIColor.red, NSAttributedString.Key.paragraphStyle: paragraphStyle])
someLabel.attributedText = attributedTitle
For lack of a better solution I've written the following function that empirically tries to find a font size that doesn't split a word in the middle. It's hackish but at least it works... Comments, suggestions or any better solution welcome!
func findFittingFont(for label: String) -> UIFont {
// note: the 'split' function is a personal addition that splits a string on a regex
// in this case, split between a non-word char and a word char
let words = label.split(pattern: "(?<=\\W)(?=\\w)")
var font = myLabel.font!
// the width we don't want to overflow
let maxWidth = myLabel.frame.width
var fontSize = myLabel.frame.height
var tooBig: Bool
repeat {
tooBig = false
font = font.withSize(fontSize)
// check for each word whether the rendered width is larger than the max width
for word in words {
// calculate the rendered width with the current font
let width = (word as NSString).size(withAttributes: [.font: font]).width
if width > maxWidth {
tooBig = true
// decrease the size by a factor
fontSize *= 0.9
break
}
}
// go on as long as there's an overflowing word
}
while tooBig
return font
}

Get the size of wrapped text in Label

If I create a Label in a 500x500 area with wordwrap, how can I find out the height of the wrapped text ? I'm looking for the yellow height, not the salmon height.
Answer of #idrise doesn't work for system font And here I give a more flexible answer.
Assume we want to create a text/label which has a fixed width, but dynamic height according to text's length. for that you can use below code:
Label *lbl = Label::createWithSystemFont("aaa aaa aaa aaa aaa aaa", "Arial", 50);
lbl->setDimensions(FIXED_WIDTH, 0); // "0" means we don't care about wrapping vertically, hence `getContentSize().height` give a dynamic height according to text's length
////
auto dynamicHeight = title->getContentSize().height; // According to text's length :)
And obviously for fixed height you can do similarly.
Hope Help someone :]
This may seem a little counter intuitive.
First you set the dimensions with an excessively large height.
Calling getLineHeight and getStringNumLines will calculate the height based on the width passed.
You send the width and height back to setDimensions.
Now your labels getContentSize() will return the actual size of the text.
IE
label->setDimensions(width, 2000);
label->setDimensions(width,label->getStringNumLines() *
ceil(label->getLineHeight()));
They added the functionality you want:
Added three overflow type to new label: CLAMP, SHRINK, RESIZE_HEIGHT.
Overflow type is used to control label overflow result, In SHRINK mode, the font size will change dynamically to adapt the content size. In CLAMP mode, when label content goes out of the bounding box, it will be clipped, In RESIZE_HEIGHT mode, you can only change the width of label and the height is changed automatically. For example:
//Change the label's Overflow type
label->setOverflow(Label::Overflow::RESIZE_HEIGHT);
mTexto=Label::createWithTTF(mTextoHelp.c_str(),CCGetFont(), 30);
mTexto->setHeight(100.f);
mTexto->setOverflow(Label::Overflow::RESIZE_HEIGHT);
mTexto->setDimensions(mSize.width*0.8f, 0.f);

iText -- How do I get the rendered dimensions of text?

I would like to find out information about the layout of text in a PdfPCell. I'm aware of BaseFont.getWidthPointKerned(), but I'm looking for more detailed information like:
How many lines would a string need if rendered in a cell of a given width (say, 30pt)? What would the height in points of the PdfPCell be?
Give me the prefix or suffix of a string that fits in a cell of a given width and height. That is, if I have to render the text "Today is a good day to die" in a specific font in a PdfPCell of width 12pt and height 20pt, what portion of the string would fit in the available space?
Where does iText break a given string when asked to render it in a cell of a given width?
This is with regard to iText 2.1.6. Thanks.
iText uses the ColumnText class to render content to a cell. This is explained in my book on page 98-99. This means that, just like with ColumnText, you need to make the distinction between text mode and composite mode.
In any case, ColumnText measures the width of the characters and tests if they fit the available width. If not, the text is split. You can change the split behavior in different ways: by introducing hyphenation or by defining a custom split character.
I've written a small proof of concept to show how you could implement custom "truncation" behavior. See the TruncateTextInCell example.
Instead of adding the content to the cell, I have an empty cell for which I define a cell event. I pass the long text "D2 is a cell with more content than we can fit into the cell." to this event.
In the event, I use a fancy algorithm: I want the text to be truncated in the middle and insert "..." at the place where I truncated the text.
BaseFont bf = BaseFont.createFont();
Font font = new Font(bf, 12);
float availableWidth = position.getWidth();
int contentLength = content.length();
int leftChar = 0;
int rightChar = contentLength - 1;
availableWidth -= bf.getWidthPoint("...", 12);
while (leftChar < contentLength && rightChar != leftChar) {
availableWidth -= bf.getWidthPoint(content.charAt(leftChar), 12);
if (availableWidth > 0)
leftChar++;
else
break;
availableWidth -= bf.getWidthPoint(content.charAt(rightChar), 12);
if (availableWidth > 0)
rightChar--;
else
break;
}
String newContent = content.substring(0, leftChar) + "..." + content.substring(rightChar);
PdfContentByte canvas = canvases[PdfPTable.TEXTCANVAS];
ColumnText ct = new ColumnText(canvas);
ct.setSimpleColumn(position);
ct.addElement(new Paragraph(newContent, font));
ct.go();
As you can see, we get the available width from the position parameter and we check how many characters match, alternating between a character at the start and a character at the end of the content.
The result is shown in the resulting PDF: the content is truncated like this: "D2 is a c... the cell."
Your question about "how many lines" can be solved in a similar way. The ColumnText class has a getLinesWritten() method that gives you that information. You can find more info about positioning a ColumnText object in my answer to your other question: Can I tell iText how to clip text to fit in a cell

Relative UILabel in a game object

I created a game object which acts as a repeating item for a UIGrid which I populate dynamically. The gameobject (RowItem) has couple of UILabel whose text can change on runtime depending on the content. The content of these UILabels overlap when the text is bigger. Can anybody help me in how to make UILabel expand relative to the adjacent UILabel when the text is more/less?
You can use transform.localScale property of the UILabel's property to scale it. Just make them bigger when the text is bigger than let's say 20 characters. Try with arbitrary values.
Also when you change the scale, run a re-align method, which aligns other labels so that they don't overlap.
you can get the text length in pixel by this:
UILabel label;
float width = label.relativeSize.x * label.transform.localScale.x;
float height = label.relativeSize.y * label.transform.localScale.y;
Let's say that you want to set you max length to 100, you can do this:
if (width > 100)
{
label.localScale = new Vector3(100 / label.relativeSize.x, 100/ label.relativeSize.x, 1);
}
the second param for Vector3 is also based on relativeSize.x is not a typo, that makes sure your text will not become thin.
Hope this works.

Split String at specific line for NSString

Hello ist there a way to split a String for UITableView at a specific line to put the "rest" (the second part of the data) in an own cell
NSString *data;
CGsize *size = [data sizeOfStringWithFont:[UIFont systemFontOfSize:14] constrainToWidth:280.0];
if the size.height e.g. is greater than 1500 i want to split the string at this line position!
thank you
Use "constrainedToSize" (instead of just to width) and render as much as you can.
If you really want to take exactly the text that would not fit, you're going to have to do essentially a search, adding a word at a time and then doing the size check to see how high you have gotten. You could start out with a rough estimate by doing the whole string constrained to something only one line high with boundless width (say 999999) and then divide up the width into however many rows you are wishing to allow to get a rough starting point for adding/removing words from the string (it will not be exact because of word wrapping).
Fundamentally though it seems wierd to take the leftover text and put it in another cell. Are you really sure you don't simply want to change the height of the cell with the text to allow it to fit the whole thing?
I think Kendall has the right idea, but the constrained sizes should be reversed to get the exact height based on word wrapping. Take a sample CGSize that is the same width as your cell, but with a height larger than the max height you expect. In the sample code below, textSize will contain the height of your string as it would appear in your cell with an unbounded height.
CGSize sz = CGSizeMake (
yourCellWidth,
999999.0f );
CGSize textSize = [yourString sizeWithFont:yourCellfont
constrainedToSize:sz
lineBreakMode:UILineBreakModeWordWrap];
If the height is greater than 1500, you could start picking off substrings (substringWithRange) from the end and measuring them like above until you get something >= the remainder above 1500 that was returned by textSize.