UILabel truncate middle lines in multi-line label - ios

I have a UILabel that has 3 lines max. Here is the code:
_testLabel = [[UILabel alloc] init];
_testLabel.text = #"Line 11111111111111\nLine 2\nLine 3";
_testLabel.numberOfLines = 3;
_testLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[_testLabel sizeToFit];
_testLabel.width -= 5;
By calling sizeToFit and then subtracting 5, I cause line 1 to wrap. This in turn pushes line 3 outside of the allowed bounds, so at the end of "Line 2" there is a "..." and line 3 is not shown.
Instead of wrapping line 1 and truncating line 3, what I really want it to truncate line 1. This way, any lines that are too long to fit in the specified width will be truncated and NOT wrapped.
Is there a way to achieve this?
The best I can think of is to split the string at every '\n' char, and have a separate UILabel for each line with numberOfLines set to 1 for each.
EDIT:
To be more clear, here is what the label looks like with the example code above:
Line
11111111111111
Line 2...
And here is what I would like it to look like:
Line 11111111111...
Line 2
Line 3

Check the enumerators for NSLineBreakMode
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/ApplicationKit/Classes/NSParagraphStyle_Class/Reference/Reference.html#//apple_ref/c/tdef/NSLineBreakMode

You probably want to calculate the size of the UILabel before you set its text, then tweak your line break settings.
See NSString's method:
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context

You are doing it right . You need only one other line .
set "adjustsFontSizeToFitWidth" with "false" in order to prevent the label from fitting width by changing font size.

Related

Get truncated text from UILabel in Swift [duplicate]

I have a single line UILabel. It has width = screen width and the content now is (the content of UILabel can change)
You have 30 seconds to make an impression during an interview
Currently, my UILabel is truncated tail and the word "duration" is not complete
self.nameLabel.lineBreakMode = NSLineBreakByTruncatingTail;
What I want is I want my UILabel still truncating tail and only display complete word.
Like the image below
Any help or suggestion would be great appreciated.
You can do something like this:
let labelWidth = CGRectGetWidth(label.bounds)
let str = "You will have 30 seconds till you give us a good impression" as NSString
let words = str.componentsSeparatedByString(" ")
var newStr = "" as NSString
for word in words{
let statement = "\(newStr) \(word) ..." as NSString
let size = statement.sizeWithAttributes([NSFontAttributeName:label.font])
if size.width < labelWidth {
newStr = "\(newStr) \(word)"
}
else{
break
}
}
newStr = newStr.stringByAppendingString(" ...")
self.label.text = newStr as String
Idea is: we split words and try check the width while appending from the beginning + the string "..." till we found the a word that will exceed the size, in the case we stop and use this new string
Ideally this is not possible,with default UILabel, when you set lineBreakMode to TruncatingTail, depending on the space required by the letter/word the OS will truncate it, one solution to fix the issue you can use following properties depending on your match.
Minimum Font Scale -- Use this property to specify the smallest multiplier for the current font size that yields an acceptable font size to use when displaying the label’s text. If you specify a value of 0 for this property, the current font size is used as the smallest font size.
Minimum Font Size -- When drawing text that might not fit within the bounding rectangle of the label, you can use this property to prevent the receiver from reducing the font size to the point where it is no longer legible.
i am not sure but try it:
nameLabel.adjustsFontSizeToFitWidth = NO;
nameLabel.lineBreakMode = NSLineBreakByWordWrapping;
OR
If you are using storyboard follow these steps i tried this and it working fine
Open Attribute Inspector
Change Line Breaks to Truncate Tail then
Change AutoShrink to Minimum Font Size
here are my screenshots of label after and before applying these properties
new output

How to make multi-line UILabel text fit within predefined width without wrapping mid-word

I have a UILabel carefully laid out in Interface Builder with proper height and width constraints. The number of lines is set to 4. The wrapping is set to word wrap. The text is "CHECKED". The font size is very large and thus it only fits "CHECKE" and the "D" is on the second line. Writing "Checked" instead of "CHECKED" lets the font shrink (as intended) so that the whole word fits. But (the text is user given and it can be expected that the user writes fully uppercase words) having uppercase words the label does not break it/shrink the font as expected.
Do you have a suggestion as to what I might have missed? Capitalising the words (thusly only having the first letter uppercase) does work, but is not what the client wants.
Updated question
The problem seems to be unrelated to having uppercase or lowercase text. My problem could be solved by an answer to the following question:
How to make (ideally with the help of only Interface Builder) the UILabel text shrink trying to fit full words within all available lines without wrapping the text mid-word?
If the text "CHECKED" is too wide for a label (with more than 1 line available) it should shrink the font size instead of breaking the "D" and wrapping the single letter to the next line.
If the text is "one CHECKED two" and the single word "CHECKED" is already too wide for a label (with more than 1 line available) it should break between all words and shrinking the font size so that "CHECKED" still fits the middle line.
Avoiding:
one
CHECKE
D two
Thank you very much!
Here is a UILabel subclass that will find the largest word in the labels text, use the boundingRect function of NSString to see how large that one word will be with the current font, and drop the font size until it fits the width.
class AutosizingMultilineLabel: UILabel {
override func layoutSubviews() {
super.layoutSubviews()
self.adjustFontToFitWidth()
}
func adjustFontToFitWidth() {
guard let currentFont = self.font else { return }
let minimumFontSize: CGFloat = floor(self.minimumScaleFactor * currentFont.pointSize)
var newFontSize = currentFont.pointSize
var theNewFont = currentFont
if let text = self.text, let longestWord = text.components(separatedBy: " ").max(by: {$1.count > $0.count})?.replacingOccurrences(of: "\n", with: "") {
let nsString = longestWord as NSString
while newFontSize > minimumFontSize {
theNewFont = currentFont.withSize(newFontSize)
let boundingRect = nsString.boundingRect(with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude),
options: NSStringDrawingOptions.usesLineFragmentOrigin,
attributes: [.font: theNewFont],
context: nil)
if ceil(boundingRect.size.width) <= self.bounds.size.width {
break
}
newFontSize -= 1
}
self.font = theNewFont
}
}
}
When the word is bigger than the line, word wrap doesn't work. If it doesn't fit on this line, it won't fit on the next line. (same word, same size, same line size). To make it fit, the label will start putting letters on the next line.
If you allow multiple lines on your label, the OS will try to fill the lines before adjusting the font size.
I think you're just running into a limitation on Autoshrink.
In Interface Builder:
add a new UILabel with Width: 230 and Height: 280
set the Font to System 44.0
set Line Break: Truncate Tail
set Autoshrink: Minimum Font Scale at 0.15
set the text of the label to test CHECKED lines
Now, drag the handle on the right edge of the label left and right... when it gets too narrow, the word CHECKED will break onto the next line.
Change CHECKED to checked and do the same thing. You should see the same behavior.
Now, try dragging the Bottom edge up and down. With either CHECKED or checked, you should see the Font Size auto shrink.
So... to do what you're trying to do, you might have to skip Autoshrink and instead do some code calculations.
Edit: further visual of what goes on...
Start with above values, but set the Height of the label to 170 - gives it just a little vertical padding.
Now, drag the left edge to make it narrower.
When you reach the end of the word CHECKED, and keep going, you will see the font shrink until it gets small enough that there is space for it to wrap to a 4th line.
I think you're going to need some code to get exactly what you need.

UILabel truncate tail and skip not complete word

I have a single line UILabel. It has width = screen width and the content now is (the content of UILabel can change)
You have 30 seconds to make an impression during an interview
Currently, my UILabel is truncated tail and the word "duration" is not complete
self.nameLabel.lineBreakMode = NSLineBreakByTruncatingTail;
What I want is I want my UILabel still truncating tail and only display complete word.
Like the image below
Any help or suggestion would be great appreciated.
You can do something like this:
let labelWidth = CGRectGetWidth(label.bounds)
let str = "You will have 30 seconds till you give us a good impression" as NSString
let words = str.componentsSeparatedByString(" ")
var newStr = "" as NSString
for word in words{
let statement = "\(newStr) \(word) ..." as NSString
let size = statement.sizeWithAttributes([NSFontAttributeName:label.font])
if size.width < labelWidth {
newStr = "\(newStr) \(word)"
}
else{
break
}
}
newStr = newStr.stringByAppendingString(" ...")
self.label.text = newStr as String
Idea is: we split words and try check the width while appending from the beginning + the string "..." till we found the a word that will exceed the size, in the case we stop and use this new string
Ideally this is not possible,with default UILabel, when you set lineBreakMode to TruncatingTail, depending on the space required by the letter/word the OS will truncate it, one solution to fix the issue you can use following properties depending on your match.
Minimum Font Scale -- Use this property to specify the smallest multiplier for the current font size that yields an acceptable font size to use when displaying the label’s text. If you specify a value of 0 for this property, the current font size is used as the smallest font size.
Minimum Font Size -- When drawing text that might not fit within the bounding rectangle of the label, you can use this property to prevent the receiver from reducing the font size to the point where it is no longer legible.
i am not sure but try it:
nameLabel.adjustsFontSizeToFitWidth = NO;
nameLabel.lineBreakMode = NSLineBreakByWordWrapping;
OR
If you are using storyboard follow these steps i tried this and it working fine
Open Attribute Inspector
Change Line Breaks to Truncate Tail then
Change AutoShrink to Minimum Font Size
here are my screenshots of label after and before applying these properties
new output

UILabel with 2 lines, how to truncate each line independently?

I'm trying to display a UILabel with 2 lines with something like this:
"Here is the first line (a long one) and that's it"
"And this is the second line with random number of chars"
With Truncate Tail it displays this:
"Here is the first line (a long one) and that's ..."
My goal is to display:
"Here is the first line (a long .."
"And this is the second line wit.."
Is there a way to do this with UILabel set to 2 lines and without using 2 UILabel's ?
Split the string on \n to create two strings. Then create 2 UILabels set to numberOfLines = 1 and lineBreakMode = .ByTruncatingTail. Lay them out in the view, one on top of the other.
First, you need to set the numbers of lines to 0(infinite number of lines);
textLabel.numberOfLines = 0;
Next, you can break wherever you want by doing this:
textLabel.text = "str1 \n str2"

UILabel lineBreakMode, break at specific character

Is there a way to break the text in a UILabel at a specific character say ";" ??
I don't want it to be broken with Word Wrap or character Wrap.
Sure, just replace all the occurrences of ";" with ";\n" before you show the string.
There is another way which will work in limited circumstances. You can replace your normal spaces (\U+0020) with non-breaking spaces (\U+00A0). This will allow you to limit the number of places your string breaks. For example if you had a string like;
I have a string with two sentences. I want it to preserve the sentences.
By carefully using non-breaking spaces you can get it to break like this;
I have a string with two sentences.
I want it to preserve the sentences.
HOW:
For Strings in InterfaceBuilder:
Go to System Preferences -> Keyboard -> Input Sources -> +
Select the category 'Others' and the keyboard 'Unicode Hex Input'
Select 'Add'
Make sure 'Show Input method in menu bar' is selected.
CLose System Preferences
Go back to XCode and find your string
Using the keyboard menu in your menu bar, select the Unicode keyboard
In your string select a space. Type Option+00a0. A space will appear when you've completed the 4 digit sequence. That space is a non-breaking space. Repeat for all spaces you need to.
For programmatic strings you can just add \U00A0 as appropriate.
You can use (\r) instead of newline (\n) to create a line break.
Set numberOfLines to 0 to allow for any number of lines.
yourLabel.numberOfLines = 0;
Like With in your case just replace ; with ;\n
NSString *string = #"This; is a; NSString";
string = [string stringByReplacingOccurrencesOfString:#";"
withString:#";\n"];
You can't break line using ; this character. if you want to break line then replace this character with \n character.
label.text=[label.text stringByReplacingOccurrencesOfString:#";" withString:#"\n"];
And make
label.numberOfLines = 0.
And Update the label frame
CGSize labelSize = [label.text sizeWithFont:label.font constrainedToSize:label.frame.size lineBreakMode:label.lineBreakMode];
label.frame = CGRectMake(label.frame.origin.x, label.frame.origin.y, label.frame.size.width, labelSize.height);
You can do with combination of newline character and line break.Check the following code,
self.testLabel.text = #"abc;\nabc;";
self.testLabel.numberOfLines = 0;
CGSize labelSize = [self.testLabel.text sizeWithFont:self.testLabel.font
constrainedToSize:self.testLabel.frame.size
lineBreakMode:self.testLabel.lineBreakMode];
self.testLabel.frame = CGRectMake(
self.testLabel.frame.origin.x, self.testLabel.frame.origin.y,
labelSize.width, labelSize.height);

Resources