Multiline label in UIStackView
The correct answer is here:
https://stackoverflow.com/a/43110590/566360
- Embed the
UILabel
inside aUIView
(Editor -> Embed In -> View) - Use constraints to fit the
UILabel
to theUIView
(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.
Horizontal UIStackView with two label (one multiline label, one one-line)
To try and simplify...
Forget calculating any widths... what matters is the horizontal Content Hugging
and Content Compression Resistance
Leave the left (blue) label at the defaults:
Content Hugging Priority
Horizontal: 251
Content Compression Resistance Priority:
Horizontal: 750
But set the right (orange) label to:
Content Hugging Priority
Horizontal: 1000
Content Compression Resistance Priority:
Horizontal: 1000
Results:
The only other issue would be if the text in the right-side label exceeds the full width of the view -- but you haven't indicated that you might need that much text.
UIStackView and Multiline Labels?
This really seems to be a bug.
A workaround that works for me is embedding the multiline label in a view and leaving it there.
That fixes the layout on the Storyboard editor and also works in the simulator.
A weird thing is that if I have several multiline labels on the same StackView I only have to embed one of them in a UIView, and then all the other multiline labels will behave properly.
UIStackView distribution and alignment of a multiline UILabel
UPDATE
Since you're setting titleLabel.numberOfLines = 3
, one way to do this is simply to append three newlines to the title text. That will force titleLabel
to always consume its full height of three lines, forcing timeLabel
to the bottom.
That is, when you set titleLabel.text
, do it like this:
titleLabel.text = theTitle + "\n\n\n"
ORIGINAL
If you let one of the labels stretch vertically, the stretched label's text will be centered vertically within the stretched label's bounds, which is not what you want. So we can't let the labels stretch vertically. Therefore we need to introduce a padding view that can stretch but is otherwise invisible.
If the padding view gets squeezed down to zero height, the stack view will still put spacing before and after it, leading to double-spacing between titleLabel
and timeLabel
, which you also don't want.
So we'll need to implement all the spacing using padding views. Change verticalStackView.spacing
to 0.
Add a generic UIView
named padding1
to verticalStackView
after categoryLabel
, before titleLabel
. Constrain its height to equal 10.
Add a generic UIView
named padding2
to verticalStackView
after titleLabel
, before timeLabel
. Constrain its height to greater than or equal to 10 so that it can stretch.
Set the vertical hugging priorities of categoryLabel
, titleLabel
, and timeLabel
to required, so that they will not stretch vertically.
Constrain the height of verticalStackView
to the height of containerStackView
so that it will stretch one or more of its arranged subviews if needed to fill the vertical space available. The only arranged subview that can stretch is padding2
, so it will stretch, keeping the title text near the top and the time text at the bottom.
Also, constrain your containerStackView
to the bounds of contentView
and set containerStackView.translatesAutoresizingMaskIntoConstraints = false
.
Result:
Here's my playground:
import UIKit
import PlaygroundSupport
class MyCell: UICollectionViewCell {
var containerStackView: UIStackView!
var verticalStackView: UIStackView!
var categoryLabel: UILabel!
var titleLabel: UILabel!
var timeLabel: UILabel!
var imageView: UIImageView!
func createSubViews() {
// contains the UIStackview with the 3 labels and the UIImageView
containerStackView = UIStackView()
containerStackView.axis = .horizontal
containerStackView.distribution = .fill
containerStackView.alignment = .top
contentView.addSubview(containerStackView)
// the UIStackView for the labels
verticalStackView = UIStackView()
verticalStackView.axis = .vertical
verticalStackView.distribution = .fill
verticalStackView.spacing = 0
containerStackView.addArrangedSubview(verticalStackView)
categoryLabel = UILabel()
categoryLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
categoryLabel.textColor = UIColor.lightGray
verticalStackView.addArrangedSubview(categoryLabel)
let padding1 = UIView()
verticalStackView.addArrangedSubview(padding1)
titleLabel = UILabel()
titleLabel.numberOfLines = 3
titleLabel.lineBreakMode = .byWordWrapping
verticalStackView.addArrangedSubview(titleLabel)
let padding2 = UIView()
verticalStackView.addArrangedSubview(padding2)
timeLabel = UILabel()
timeLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
timeLabel.textColor = UIColor.lightGray
verticalStackView.addArrangedSubview(timeLabel)
// UIImageView
imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.layer.cornerRadius = 5
layer.masksToBounds = true
containerStackView.addArrangedSubview(imageView)
categoryLabel.setContentHuggingPriority(.required, for: .vertical)
titleLabel.setContentHuggingPriority(.required, for: .vertical)
timeLabel.setContentHuggingPriority(.required, for: .vertical)
imageView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
containerStackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
contentView.leadingAnchor.constraint(equalTo: containerStackView.leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: containerStackView.trailingAnchor),
contentView.topAnchor.constraint(equalTo: containerStackView.topAnchor),
contentView.bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor),
verticalStackView.heightAnchor.constraint(equalTo: containerStackView.heightAnchor),
padding1.heightAnchor.constraint(equalToConstant: 10),
padding2.heightAnchor.constraint(greaterThanOrEqualToConstant: 10),
])
}
}
let cell = MyCell(frame: CGRect(x: 0, y: 0, width: 320, height: 110))
cell.backgroundColor = .white
cell.createSubViews()
cell.categoryLabel.text = "MY CUSTOM LABEL"
cell.titleLabel.text = "This is my title"
cell.timeLabel.text = "3 days ago"
cell.imageView.image = UIGraphicsImageRenderer(size: CGSize(width: 110, height:110)).image { (context) in
UIColor.blue.set()
UIRectFill(.infinite)
}
PlaygroundPage.current.liveView = cell
Horizontal StackView issue for multiline label and fixed size icon image
Working Constraints.!
- Set inner Stack view Alignment to Top and Distribution set to
Fill Proportionally - Set width of icon to fixed of 20px(No Aspect ratio and no
Height) - Set icon Content Compression Resistance property Horizontal
priority to 1000.
Images attached
iOS: UILabel multi-line text get truncated in horizontal UIStackView
alignment
/* The layout of the arrangedSubviews transverse to the axis;
e.g., leading/trailing edges in a vertical stack
*/
@property(nonatomic) UIStackViewAlignment alignment;
For .fill
StackView will automatically generate these layout attributes
for you.
imageView.bottom == label.bottom
imageView.top == label.top
the size of stackView depend on the 'voice' image
's size or size layout attributes of imageView
as it is fixed instead of adjustable UILable
which namely have the low priority.
For .center
StackView achieve a decent height by look for the max height between your label
and imageView
.
_UILayoutSpacer.bottom >= imageView.bottom
_UILayoutSpacer.bottom >= label.bottom
_UILayoutSpacer.top <= imageView.top
_UILayoutSpacer.top <= label.top
Related Topics
How to Call Method from Viewcontroller in Gamescene
Uitextfield in Uialertcontroller (Border, Backgroundcolor)
Replace Occurrences of Nsnull in Nested Nsdictionary
This Class Is Not Key Value Coding-Compliant for the Key Xxxxxx
Xcode: Failed to Get the Task for Process
Hide Keyboard When Scroll Uitableview
Detect Backspace Event in Uitextfield
Using Cabasicanimation to Rotate a Uiimageview More Than Once
Uitableviewcell Set Selected Initially
Storyboard Segues Causing Memory Leaks
Google Places Autocomplete on iOS - Can't Load Search Results - Try Again
Sending a Private Message to Your Friends via Facebook iOS Sdk
What Is Most Common and Correct Practice to Get a Cgfloat from an Nsnumber
How to Get Core Data Object from Specific Object Id
How to Draw a Circle in iOS Swift
How to Figure Out Which Url Is Being Blocked by App Transport Security