UILabel and UITextView line breaks don't match
It was a change to UILabel
in IOS 11 to fix orphaned words. No way to shut it off although I wish there was. The only way to do it would be to use a non editable/nonscrollable textview or a CATextLayer
. To get the sizing attributes of a UILabel
I am afraid you have to do that manually.
See this as the problem was similar. Although not in that answer I did find that UITextView
wraps the old way. I am using a UITextView
now and I manually adjust the font size in textDidChange
. I hold the original font so I can always resize.
Table view cell labels are inconsistent in line break
An "orphan" is a single word on a line at the end of a paragraph. In typography, it is considered "bad layout" to leave a word dangling at the end.
So, Apple has coded UILabel
to avoid "orphans."
If you don't mind orphans in your labels, UITextView
does not enforce that -- so you could use a UITextView
(with editing and scrolling disabled).
As you can see in this image, though:
UITextView
has default "insets" for the text from the actual frame.
So, here is an @IBDesignable
subclass that removes the insets:
@IBDesignable
class TextViewLabel: UITextView {
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
isScrollEnabled = false
isEditable = false
isSelectable = false
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
}
}
Edit for clarification...
Here are three default UILabel
, all the exact same width:
Note that with label A, there is plenty of room at the end of the first line for the word "an"
, but it gets wrapped to the second line to prevent an orphan -- that is, prevent a single word on the second line.
With label B, the word "an"
stays on the first line, because word wrapping results in two words on the second line.
As we see with label C though, if the text wraps onto more than two lines the UILabel
will allow an orphan -- a single word on the last line.
By replacing the default UILabel
with the TextViewLabel
subclass, we can force the two-line wrapping to ignore the "orphan" wrapping:
UITextView/UILabel with background but with spacing between lines
After some research I found the best solution for what I needed.
The solution below is only iOS7+.
First we add this to - (void)drawRect:(CGRect)rect
of your UITextView
subclass.
- (void)drawRect:(CGRect)rect
/// Position each line behind each line.
[self.layoutManager enumerateLineFragmentsForGlyphRange:NSMakeRange(0, self.text.length) usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer *textContainer, NSRange glyphRange, BOOL *stop) {
/// The frame of the rectangle.
UIBezierPath *rectanglePath = [UIBezierPath bezierPathWithRect:CGRectMake(usedRect.origin.x, usedRect.origin.y+3, usedRect.size.width, usedRect.size.height-4)];
/// Set the background color for each line.
[UIColor.blackColor setFill];
/// Build the rectangle.
[rectanglePath fill];
}];
}];
Then we set the line spacing for the UITextView:
- (CGFloat)layoutManager:(NSLayoutManager *)layoutManager lineSpacingAfterGlyphAtIndex:(NSUInteger)glyphIndex withProposedLineFragmentRect:(CGRect)rect
{
return 15;
}
The method above is only called if you set the NSLayoutManagerDelegate
. You could do that in your init
, initWithFrame
and initWithCode
methods like this:
self.layoutManager.delegate = self;
Also don't forget to declare that your subclass is a delegate in your .h
file:
@interface YOUR_SUBCLASS_OF_UITEXTVIEW : UITextView <NSLayoutManagerDelegate>
Multiple lines of text in UILabel
Set the line break mode to word-wrapping and the number of lines to 0
:
// Swift
textLabel.lineBreakMode = .byWordWrapping
textLabel.numberOfLines = 0
// Objective-C
textLabel.lineBreakMode = NSLineBreakByWordWrapping;
textLabel.numberOfLines = 0;
// C# (Xamarin.iOS)
textLabel.LineBreakMode = UILineBreakMode.WordWrap;
textLabel.Lines = 0;
Restored old answer (for reference and devs willing to support iOS below 6.0):
textLabel.lineBreakMode = UILineBreakModeWordWrap;
textLabel.numberOfLines = 0;
On the side: both enum values yield to 0
anyway.
How to make UITextLabel text l in a vertical UIStackView line break on specific words
Some questions/ideas:
Did you try putting a linebreak in the string itself? A la
Enter your phone\n number
You must make sure that the width of the label is sufficient.
Don't forget to set the
lineBreakMode
of the label to.byWordWrapping
If for some reason the above doesn't work, then I'd recommend using another inner vertical UIStackView
with two labels to enforce this constraint.
How can I make a UITextView layout text the same as a UILabel?
I took the solution for line spacing found at this link and applied it to your issue. I managed to get it incredibly close by adjusting the lineSpacing
property. I tested with HelveticaNeue size 13 and managed to get it to line up as shown in the screen shot below.
textView.textContainer.lineFragmentPadding = 0;
textView.textContainerInset = UIEdgeInsetsZero;
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineSpacing = -0.38;
NSDictionary *attrsDictionary =
@{ NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue" size:13.0f],
NSParagraphStyleAttributeName: paragraphStyle};
textView.attributedText = [[NSAttributedString alloc] initWithString:textView.text attributes:attrsDictionary];
How to add line break for UILabel?
Use \n
as you are using in your string.
Set numberOfLines to 0 to allow for any number of lines.
label.numberOfLines = 0;
Update the label frame to match the size of the text using sizeWithFont:
. If you don't do this your text will be vertically centered or cut off.
UILabel *label; // set frame to largest size you want
...
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);
Update : Replacement for deprecated
sizeWithFont:constrainedToSize:lineBreakMode:
Reference, Replacement for deprecated sizeWithFont: in iOS 7?
CGSize labelSize = [label.text sizeWithAttributes:@{NSFontAttributeName:label.font}];
label.frame = CGRectMake(
label.frame.origin.x, label.frame.origin.y,
label.frame.size.width, labelSize.height);
Related Topics
Why Is -Force_Load No Longer Required for My Three20 Dependencies in Xcode 4.2
Autolayout: Uiview Within Uiview Has Incorrect Frame
Simple Sprite Kit Scene Setup Going Wrong
Re-Assigning Instance of Avaudioplayer in iOS13 Leads to Bad_Access Runtime Error
How to Pass Params on Timer Selector
Swift 3.0 Multiple Selection with Select All Cell
iOS 10.3 Beta 3 Doesn't Persist Data of Keychainitem
How to Tell If an iOS Device Has a Gps
Swift: Load Images Async in Uitableviewcell
Open a Wkwebview Target="_Blank" Link in Safari
Fit Image of Random Size into a Uiwebview (Ios)
Ios: Diddiscoverperipheral Not Called in Background Mode
Make Uisearchcontroller Search Bar Automatically Active
How to Record Actual Sound on the Simulator Using Mic
Keep Getting "Unbalanced Calls to Begin/End Appearance Transitions for <Viewcontroller>" Error
How to Have Firebase Automatically Delete Values Older Than 30 Minutes