173

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?

Boon
  • 37,606
  • 51
  • 186
  • 296

21 Answers21

234

The correct answer is here:
https://stackoverflow.com/a/43110590/566360


  1. Embed the UILabel inside a UIView (Editor -> Embed In -> View)
  2. Use constraints to fit the 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.

pkamb
  • 26,648
  • 20
  • 124
  • 157
Andy
  • 3,539
  • 1
  • 16
  • 16
  • 46
    It's so stupid but it works ! Thanks man ! Maybe one day Apple will release something that is not half broken in their SDK... – Guillaume L. Feb 28 '17 at 10:33
  • For the set up I was working with, this worked like a charm. I agree, silly. But it worked. Moving on. :) – Canucklesandwich Mar 21 '17 at 20:02
  • 1
    Oh man! You are a savior! I had been at it for hours :) Remember to also set the lines to 0 in Attributes Inspector, was missing just that. – absin Dec 19 '17 at 13:15
  • I've had good luck toggling the `hidden` property on the `UIStackView` in question. It seems to force a recalculation of the layout attributes. – Matt Robinson Feb 12 '18 at 17:35
  • 9
    this is workaround, the correct solution is here https://stackoverflow.com/a/43110590/566360 – vietstone Apr 16 '18 at 03:39
  • @vietstone Thank you. Embedding the `UILabel` in a `UIView` + setting the `UIStackView's` horizontal alignment to **center** got me over the finish line – Adrian Jun 19 '18 at 12:13
  • 3
    Was sufficient for me just to change the horizontal alignment to center. No `UIView` shenanigans necessary. – shim Jun 25 '18 at 22:49
  • This worked fine for me with a vertical `UIStackView` contains 2 multiline labels and 2 images. – Alex Jul 24 '18 at 10:18
  • I just love you, Im wasting some many time on this issue, and this really solve my problem with UILabel and text on top left. – Daniel Beltrami Aug 02 '18 at 18:32
  • You shouldn't set any of `UIStackView` subview's constraints for alignment like: leading, trailing, top, bottom. The `UIStackView` is responsible for that already (it deals with arranged sub views alignments and spacing). If you want more fine control of the positioning, add your `UILabel` to a dummy transparent `UIView` and then set positioning constraints there. – Lucien Feb 18 '19 at 20:20
  • 1
    @Lucien this is exactly what was said in this answer. but the correct answer is here: https://stackoverflow.com/a/43110590/566360 – Andy Feb 20 '19 at 06:27
  • 3+ years later this answer saved a day! Why oh why Apple doesn't point this out in their documentation?! Thanks, @Andy! – jannolii Oct 02 '19 at 13:22
  • Downvoting as even though this solution works, it's a hack. See @pbm answer for correct solution. – thecloud_of_unknowing Feb 26 '20 at 10:31
  • Marking as correct answer as it works when you need to indent labels inside a stack view. They will need a container UIView. Indenting using stackview as the superview causes constraint issues. Only solution that works in all cases. – Peter Suwara Dec 31 '20 at 11:20
176

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 intrinsic​Content​Size 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.

Iulian Onofrei
  • 7,489
  • 8
  • 59
  • 96
pbm
  • 4,141
  • 3
  • 15
  • 26
  • 3
    This is the proper solution without hacks. Reading the docs always pays off! – Islam Q. Oct 14 '17 at 19:59
  • 3
    This should be the accepted solution. With the UIView workaround UILabel's text floated over another subview in my case. –  Feb 11 '18 at 08:52
  • 23
    It does not work actually... I have 3 labels in my stackview. I've set lines to zero and alignment to center... I'm still seeing the labels are cut out. – MatterGoal Apr 19 '18 at 12:49
  • Did you set the label Desired width to Explicit ? – claude31 Jul 10 '18 at 07:21
  • This is the correct solution. However, you don't have to use `.center` alignment, anything besides `.fill` should work – Linh Ta Sep 10 '18 at 06:53
  • 11
    It's the `.alignment` of the UIStackView that you have to set to anything but `.fill` *not* the UILabel, which is a little confusing in the example given `label.alignment = .center`. I think this should be `stackView.alignment = .center` – ae14 Sep 25 '18 at 19:35
  • 1
    The alignment of my stack view is `.center`, my label `numberOfLines` is `0`, yet it only shows 2 lines and not the rest. Does this solution not show something about compression or hugging resistance, maybe? – regina_fallangi Dec 04 '18 at 17:16
  • 1
    This worked for me, but didn't "look" like it was working in the storyboard, so make sure to run it before ruling it out. – J Derbs Mar 28 '19 at 22:51
  • Thank you, sir! I don't know why I had my alignment on `.fillProportionally` – Aly Yakan Mar 31 '19 at 19:36
  • Helped but still acting strange.. the labels didn't expand all the way. Had to resort @Andy's solution. – TruMan1 Apr 15 '19 at 05:09
  • For those who use storyboard, when you click the stack view, in Attributes Inspector, `Stack View > Alignment > Center` should work. I use Xcode 11.3.1 now. – Brian Hong Jan 29 '20 at 19:08
  • 3
    But how can I make a multiple lines UILableView work in vertical StackView? – DàChún Apr 11 '20 at 20:17
  • For me, this solution and Andy's solution, both didn't work. I have two labels in a stackview. One should be multiline and one has static text. the stackview is inside a tableview cell. I have tried everything i could. Please help me out. – Zahurafzal Mirza Jul 09 '20 at 05:09
  • `view.alignment = .top` was best for my instance – Waylan Sands Sep 14 '20 at 23:22
  • Setting the linebreakmode to wordWrap helps with this, especially in tableViewCells and during rotation. I think the label considers the lineBreakMode when calculating the intrinsicContentSize. – PhoneyDeveloper Sep 30 '20 at 18:56
44
  • First set the label number of lines to 0
  • The stack view still won't grow to 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:

screen recording

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.

shim
  • 7,170
  • 10
  • 62
  • 95
Irfan
  • 4,750
  • 1
  • 27
  • 31
  • 5
    What if the stack view's width is not fixed but instead is proportional to the device (i.e. superview's) width. – OutOnAWeekend May 03 '16 at 21:26
  • Yes, we can have a proportional width too. The point is if label have to break after some width whatever it is but need to be defined. – Irfan May 04 '16 at 05:10
  • @AnandKumar you can specify `preferredMaxLayoutWidth` in `viewDidLayoutSubviews` for your label in `UIViewController` or in `layoutSubviews` if you subclass `UIView`. – Shyngys Kassymov Sep 02 '16 at 11:17
  • 4
    This should have been the accepted answer, the whole point of the stackview is to not have to add constraints between the stackview and its elements... – Honey Oct 10 '17 at 15:53
  • 7
    Giving a fixed value defeats the purpose of autolayout – Islam Q. Oct 14 '17 at 19:40
  • Great answer it worked for me. The issue was simply setting a width constraint to my stack view. – user7097242 Dec 16 '17 at 20:46
  • if I don't have fixed width, but should be not more then other's view width. This still doesn't work, but its not ambiguous. Its definitely okay to calculate "grow not more than 'other's view width' – Zaporozhchenko Oleksandr Aug 15 '19 at 23:43
  • Thank you, your answer helped me to make a multiline label :) – Abrcd18 Feb 21 '21 at 10:25
29

Setting preferredMaxLayoutWidth to the UILabel worked for me

self.myLabel.preferredMaxLayoutWidth = self.bounds.size.width;
Pablo Martinez
  • 1,356
  • 11
  • 29
  • 4
    This is the correct answer in my book. When ever you mess with UILabel's and need to handle multiple lines you really need to give it a preferredMaxLayoutWidth. It might work without it, but things can get rather funky very quickly without it. – Mof Aug 10 '18 at 06:38
  • 3
    All tried all answers and all of them were wrong except yours. Thanks! – levan May 15 '19 at 12:00
  • 1
    This perfectly solved my problem and feels much less hacky, thank you. – Ethan Zhao Aug 16 '19 at 17:16
  • Thanks for your answer! In my case my stackView's alignment was already set to `.center` and I really didn't need my label inside UIView. – Fariza Zhumat Feb 04 '20 at 09:53
21

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)
}
pkamb
  • 26,648
  • 20
  • 124
  • 157
Bill Chan
  • 2,416
  • 27
  • 26
14

Just set number of lines to 0 in Attribute inspector for label. It will work for you.

enter image description here

technerd
  • 12,929
  • 9
  • 56
  • 82
5

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.

shim
  • 7,170
  • 10
  • 62
  • 95
aumansoftware
  • 835
  • 8
  • 6
5

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.

Skoua
  • 2,690
  • 3
  • 37
  • 44
5

Here's a full example of a vertical UIStackView made up of multiline UILabels 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)

enter image description here

// 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])
    }
}
zekel
  • 8,396
  • 10
  • 61
  • 94
  • This seems the solution in my case. Vertical UIStackView containing multiple UILabels with one having multi-line text. I set this SV's width proportional to the Cell's width. And that's it. – MkVal Oct 08 '20 at 15:18
4

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.

technerd
  • 12,929
  • 9
  • 56
  • 82
Sagar Daundkar
  • 190
  • 1
  • 12
3

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
shim
  • 7,170
  • 10
  • 62
  • 95
JaredH
  • 2,110
  • 1
  • 24
  • 38
1

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.

Raymond
  • 794
  • 3
  • 18
0

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)

Trung Phan
  • 885
  • 9
  • 17
0

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.

Iskeraet
  • 363
  • 3
  • 10
-1

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.

Jordan H
  • 45,794
  • 29
  • 162
  • 306
-1

Xcode 9.2:

Just set number of lines to 0. Storyboard will look weird after setting this. Don't worry run the project. While running everything will resize according to your storyboard setup. I think its kind of bug in storyboard. enter image description here

Tips : Set "number of liner to 0" in the last. reset it when you want to edit some other view and set to 0 after editing.

enter image description here

Surendra Kumar
  • 574
  • 5
  • 13
-1

What worked for me!

stackview: alignment: fill, distribution: fill, constraint proportional width to superview ex. 0.8,

label: center, and lines = 0

Michał Ziobro
  • 7,390
  • 6
  • 50
  • 99
-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

-1

It's almost like @Andy's Answer, but you can add your UILabel in extra UIStackview, vertical worked for me.

Zaporozhchenko Oleksandr
  • 3,802
  • 3
  • 18
  • 36
-1

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.

Luke
  • 6,780
  • 6
  • 39
  • 69
-1

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.

  1. Embed a UILabel in UIView.
  2. Set UILabel lines = 0.
  3. Create a stack view.
  4. Set the stack view alignment to top/bottom/center.
  5. Add constraints to elements within the stack view.

Not sure why this order of operations makes a difference, but it does.

Pranav Kasetti
  • 4,773
  • 2
  • 16
  • 38