Reputation: 41470
When putting multiline label (with linebreak set to Word Wrap) into a stack view, the label immediately loses the linebreak and displays the label text in one line instead.
Why is this happening and how does one preserve multiline label within a stack view?
Upvotes: 213
Views: 125750
Reputation: 1
This worked for me. (Swift 5)
let label = UILabel()
label.numberOfLines = 0
label.text = text // text defined somewhere
label.font = font // font defined somewhere
let height = text.height(forConstrainedWidth: stackView.width, font: font)
label.heightAnchor.constraint(equalToConstant: height).isActive = true
Then just add this label to your stackView
with addArrangedSubview
.
Upvotes: 0
Reputation: 17
You can use in your label attributedText with "\n" and relevant numberOfLines
Upvotes: -4
Reputation: 821
After trying most of the answers on this page, the solution for me was to not use a UIStackView at all.
I realized I didn't really need it and was only using it out of habit, and could accomplish the same thing with a UIView.
Upvotes: -1
Reputation: 5361
For a horizontal stack view that has a UILabel
as one of its views, in Interface Builder firstly set label.numberOfLines = 0
. This should allow the label to have more than 1 line. This initially failed to work for me when the stack view had stackView.alignment = .fill
. To make it work simply set stackView.alignment = .center
. The label can now expand to multiple lines within the UIStackView
.
The Apple documentation says
For all alignments except the fill alignment, the stack view uses each arranged view’s intrinsicContentSize property when calculating its size perpendicular to the stack’s axis
Note the word except here. When .fill
is used, the horizontal UIStackView
does NOT resize itself vertically using the arranged subviews' sizes.
Upvotes: 270
Reputation: 1258
26 November 2020, Xcode 12.2, iOS 14.2. Following works for me for vertical stack view. It works on all devices and simulators. I use storyboard and all these values are set in storyboard.
UILabel
Lines: 0
UIStackView
Alignment: Fill
Distribution: Fill Proportionally
UILabel is embedded in a view and pinned to all sides to UIView.
Upvotes: 9
Reputation: 3455
After trying all above suggestion I found no properties change is need for the UIStackView. I just change the properties of the UILabels as following (The labels are added to a vertical stack view already):
Swift 4 example:
[titleLabel, subtitleLabel].forEach(){
$0.numberOfLines = 0
$0.lineBreakMode = .byWordWrapping
$0.setContentCompressionResistancePriority(UILayoutPriority.required, for: .vertical)
}
Upvotes: 48
Reputation: 61
For those working with a storyboard or XIB file trying to embed a UILabel in a horizontal stack view, do NOT add constraints to anything that will you plan on putting in a stack view before the stack view is created. This will cause errors and/or an inability to wrap text.
Instead, do this in addition to the suggestions made by Andy and pmb.
UILabel
in UIView
.UILabel
lines = 0.Not sure why this order of operations makes a difference, but it does.
Upvotes: -1
Reputation: 4521
The correct answer is here:
https://stackoverflow.com/a/43110590/566360
UILabel
inside a UIView
(Editor -> Embed In -> View)UILabel
to the UIView
(for example, trailing space, top space, and leading space to superview constraints)The UIStackView
will stretch out the UIView
to fit properly, and the UIView
will constrain the UILabel
to multiple lines.
Upvotes: 282
Reputation: 7210
For me, the issue was that the height of the stack view was simply too short. The label and stack view were properly set up to allow the label to have multiple lines, but the label was the first victim of content compression that the stack view used to get its height small enough.
Upvotes: -1
Reputation: 4908
It's almost like @Andy's Answer,
but you can add your UILabel
in extra UIStackview
, vertical worked for me.
Upvotes: -1
Reputation: 9457
UIStackView
made up of multiline UILabel
s with automatic height.The labels wrap based on the stackview's width and the stackview's height is based on the label's wrapped height. (With this approach you don't need to embed the labels in a UIView
.) (Swift 5, iOS 12.2)
// A vertical stackview with multiline labels and automatic height.
class ThreeLabelStackView: UIStackView {
let label1 = UILabel()
let label2 = UILabel()
let label3 = UILabel()
init() {
super.init(frame: .zero)
self.translatesAutoresizingMaskIntoConstraints = false
self.axis = .vertical
self.distribution = .fill
self.alignment = .fill
label1.numberOfLines = 0
label2.numberOfLines = 0
label3.numberOfLines = 0
label1.lineBreakMode = .byWordWrapping
label2.lineBreakMode = .byWordWrapping
label3.lineBreakMode = .byWordWrapping
self.addArrangedSubview(label1)
self.addArrangedSubview(label2)
self.addArrangedSubview(label3)
// (Add some test data, a little spacing, and the background color
// make the labels easier to see visually.)
self.spacing = 1
label1.backgroundColor = .orange
label2.backgroundColor = .orange
label3.backgroundColor = .orange
label1.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi."
label2.text = "Hello darkness my old friend..."
label3.text = "When I wrote the following pages, or rather the bulk of them, I lived alone, in the woods, a mile from any neighbor, in a house which I had built myself, on the shore of Walden Pond, in Concord, Massachusetts, and earned my living by the labor of my hands only."
}
required init(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}
Here is a sample ViewController
that uses it.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let myLabelStackView = ThreeLabelStackView()
self.view.addSubview(myLabelStackView)
// Set stackview width to its superview.
let widthConstraint = NSLayoutConstraint(item: myLabelStackView, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.view, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1, constant: 0)
self.view.addConstraints([widthConstraint])
}
}
Upvotes: 5
Reputation: 1
For anyone who still cannot make it work. Try to set Autoshrink with a minimum Font Scale on that UILabel.
Screenshot UILabel Autoshrink settings
Upvotes: -2
Reputation: 11752
What worked for me!
stackview: alignment: fill, distribution: fill, constraint proportional width to superview ex. 0.8,
label: center, and lines = 0
Upvotes: -1
Reputation: 923
System layout should figure out origin, width and height to draw it subviews, in this case all of your subviews has same priority, that point make conflict, layout system don't known dependencies between views, which one draw first, second and so on
Set stack subviews compression will solve problem with multiple line, depending on your stack view is horizontal or vertical and which one you want to become multiple lines. stackOtherSubviews .setContentCompressionResistancePriority(.defaultHight, for: .horizontal)
lblTitle.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
Upvotes: 0
Reputation: 3603
The magic trick for me was to set a widthAnchor
to the UIStackView
.
Setting leadingAnchor
and trailingAnchor
won't work, but setting centerXAnchor
and widthAnchor
made the UILabel display correctly.
Upvotes: 5
Reputation: 5130
multiLine
unless you give it a fixed width. When we fix its width then it break to multiline when that width is reached as shown:If we don't give a fixed width to the stack view then things get ambiguous. How long will the stack view grow with the label (if the label value is dynamic)?
Hope this can fix your issue.
Upvotes: 47
Reputation: 855
iOS 9+
Call [textLabel sizeToFit]
after setting the UILabel's text.
sizeToFit will re-layout the multiline label using preferredMaxWidth. The label will resize the stackView, which will resize the cell. No additional constraints besides pinning the stack view to the content view are required.
Upvotes: 6
Reputation: 2398
The following is a Playground implementation of multi-line label with a line break inside a UIStackView
. It doesn't require embedding the UILabel
inside anything and has been tested with Xcode 9.2 and Swift 4. Hope it's helpful.
import UIKit
import PlaygroundSupport
let containerView = UIView()
containerView.frame = CGRect.init(x: 0, y: 0, width: 400, height: 500)
containerView.backgroundColor = UIColor.white
var label = UILabel.init()
label.textColor = .black
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "This is an example of sample text that goes on for a long time. This is an example of sample text that goes on for a long time."
let stackView = UIStackView.init(arrangedSubviews: [label])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.distribution = .fill
stackView.alignment = .fill
containerView.addSubview(stackView)
stackView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
stackView.widthAnchor.constraint(equalTo: containerView.widthAnchor).isActive = true
stackView.heightAnchor.constraint(equalTo: containerView.heightAnchor).isActive = true
PlaygroundPage.current.liveView = containerView
Upvotes: 3
Reputation: 1639
Setting preferredMaxLayoutWidth to the UILabel worked for me
self.myLabel.preferredMaxLayoutWidth = self.bounds.size.width;
Upvotes: 39
Reputation: 320
Add UIStackView
properties,
stackView.alignment = .fill
stackView.distribution = .fillProportionally
stackView.spacing = 8.0
stackView.axis = .horizontal
Instead of adding label inside UIView
which is not required.If you are using inside UITableViewCell
please, reload data on rotation.
Upvotes: 4
Reputation: 55665
In my case, I followed the previous suggestions, but my text was still getting truncated to a single line though only in landscape. Turns out, I found an invisible \0
null character in the label's text which was the culprit. It must have been introduced alongside the em dash symbol I had inserted. To see if this is also happening in your case, use the View Debugger to select your label and inspect its text.
Upvotes: -1
Reputation: 14504
Just set number of lines to 0 in Attribute inspector for label. It will work for you.
Upvotes: 14