Uilabel and Uitextview Line Breaks Don't Match

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:

Sample Image

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:

Sample Image

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:

Sample Image

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];

Screen Shot

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



Leave a reply



Submit