How to create StackView with width = largest child's width on iOS? - ios

I want to place label to the bottom of image and align them vertically. I've tried to use StackView to achieve this. And I get this:
Is there any way to show all label's text correctly? Label text can have any length.

Constrain the label and the stack view to have equal widths.
Set the horizontal content hugging priority of the label to 249 (so it can get wider if it's narrower than the image).
Set the horizontal content compression resistance of the label to 751 (so it forces the stack view to be wider if necessary).
Result:

Don't fix the width of stackview.
Change the Alignment property of stackview to Center.
The label will grow itself with the text.

Related

How to horizontally align two UILabel (with fixed and variable width)

I have been facing issues horizontally aligning two UILabel and one UIImageView like this:
First label has variable width, can be truncated if long. Second label has fixed width, it should always be aligned to right of UIImageView. It should never go off screen. UIImageView is aligned to right of first label.
I have tried embedding them in horizontal UIStackView but the image + second label always aligns to end of cell. Got the same issue when trying without UIStackView.
Please help.
You can embed both label and horizontal StackView into another horizontal stack view. Then, you'd need to set the dynamic width Label's Content Compression Resistance Priority (you can find this property at the bottom of the Size Inspector), to be smaller in order for it to shrink.
Then on the container StackView (the one that contains all views), you'd need to set constrains to top, bottom, leading to 0 to the superview and the trailing to be greater than or equal to 0, for it to not take all space of the superview, but at the same time not get offset if the content is too wide.
I hope that is clear enough!

How to use auto layout so that the following layout is achieved?

I have a vertical UIStackView whose top, leading and trailing edges are pinned to the superview and whose height is determined by its subviews.
Its arranged subviews consist of 5 horizontal UIStackViews each of which have 3 UILabels as their arranged subviews.
For each of the horizontal UIStackView, the text of the first UILabel is the heading, the text of the second UILabel is the colon and the text of the third UILabel is the info as shown in the following image
All of the UILabels have numberOfLines = 0 so that they are multiline.
I would like to create constraints using auto layout so that the following conditions are met-
The colon UILabels are all vertically aligned
The width of the info UILabels are at least 20% of parent view
The width of the info UILabels are at most 60% of parent view
If the width of all the info UILabels is less than 60% of parent view ( see 3 ), then it must shrink to the width of the widest info UILabel.
I understand how to use auto layout to create constraints to satisfy the first 3 conditions.
However, I do not understand how to create constraint to satisfy the 4th condition. I tried increasing the "Content Hugging Priority" but it shrinks the label to 20% of the parent view ( see 2 ).
Can anyone point out how to achieve this layout?
This is considerably more complex than it would appear on the surface.
The "Info" column has to be evaluated for min/max width and word-wrapping, and the "Heading" column has to be evaluated for word-wrapping based on the "Info" columns variable width... and the "Info" labels have to match widths based on the widest wrapped (or not) text.
That's a lot for auto-layout to handle.
I don't think it can be done with constraints only. The main problem being that auto-layout calculates the height of the height of the Heading labels based on the potential for the Info labels to be at their widest.
So, we can end up with this:
and the Heading label text is no longer vertically aligned to the top of the label.
The best I could come up with is to override viewDidLayoutSubviews(), using a counter which forces auto-layout to calculate the Info labels first and then the Heading labels.
As to constraint settings...
The Heading labels need nothing special - just set the number of lines to Zero to allow word-wrapping.
The "Colon" labels don't need constraints, but they do need both Horizontal Content Hugging and Compression Resistance set to 1000 (required), because we don't want them stretching or getting squeezed.
The Info labels need:
Horizontal Content Compression Resistance: 1000
Horizontal Content Hugging: 999
Width: >= its stackView Width with Multiplier 0.2
Width: <= its stackView Width with Multiplier 0.6
Width: = its stackView Width with Multiplier 0.2 and Priority: 999
and finally, the bottom 4 Info labels need Width constraint equal to the top Info label.
The horizontal stack views that make up the 5 "rows" should be set to:
Alignment: Top
Distribution: Fill
Spacing: 8 (horizontal spacing between the "columns")
The vertical stack view that holds the 5 "rows" should be set to:
Alignment: Fill
Distribution: Fill
Spacing: 10 (vertical spacing between the "rows")
I've put up a project on GitHub that demonstrates the layout -- it has 10 different "variations" of label content to show how the sizing works: https://github.com/DonMag/MutlilineVarWidthLabelColumns
you can achieve this kind of UI by using min and max constraints.
you can follow the below constraints for it.
give max width to Heading label with the relation of the screen.
give fix width to colon label
Give leading, trailing, top, bottom to info label
change Horizontal content hugging priority of info Label to 252.
please check the below images for more details.
The actual structure of my example
Constraints for Heading label
Constraints for info-label
output
If you need any help then feel free to ask again.

Dynamic height for a horizontal UIStackview

I have a horizontal StackView with distribution as "fill equally". It has 3 labels.
I want one of the labels to have dynamic height.
When I set the number of number of lines to 0 for that label, it ends up breaking the constraint the spacing constraint at runtime.
All these horizontal stackviews make a one vertical stackview.
I have tried setting lower vertical hugging priority and higher resistance priority to the multiline label
Also it behaves like this in the XIB
Before changing the number of lines
After changing the number of lines
You do not need to set Content Hugging and Compression resistance property to achieve that effect.
Horizontal Stack Views: Set Alignment "Fill" & Distribution "Fill Equally"
Vertical Stack View: Set Alignment "Fill" & Distribution "Fill"
Set Label Lines to 0.
Set Top, Bottom, Leading Constraint for Vertical Stack View.
Set all constraints for UIStackView.
Update the UIStackview height constraints constant value based on UILabel text height
Not Directly answering the question rather on how to control the dynamic height of UIStackView.
Vertical Stack View. Make it free of height like no bottom constraints or no height constraints.
Contents(like view) are fixed height.
Both Alignment & Distribution of UIStackView will "fill"
now show or hide the contents then Stactview height will dynamically change.

How to center a Label with an ImageView next to it in Swift?

I am trying to have a UIImageView next to a UILabel with dynamic content. Both within a StackView.
Currently I'm using the Fill Alignment and Distribution. Changing it to one of the other options, changes the size of the ImageView (fixed Constraints to 30x30).The StackView has left and right leading Constraints of 0.
This is my current:
The is what I want:
How do I need to set up the StackView to archive my goals? Help is very appreciated.
You can try using a vertical stack view with an embedded horizontal stack view.
The first stack view has a vertical axis. Its alignment is centre and the distribution is fill (the default). This stack view has the following constraints: Trailing to superview, leading to superview and top space to top layout guide.
The second stack view contains the image view and label. Its axis is horizontal. The fill alignment and distributions are set to their default values. The spacing is set to 10 (can be changed to suit your needs). This stack view has no constraints.
The image view has a height and width constraints of 30 respectively.
The label has no constraints.
Both the image view and label have default content hugging and compression values.
If the label has longer text then then it will appear like this:
Have you tried manipulating content hugging and content compression resistance priority ? I believe these property will let you manage the length distance between both the image view and textView, and the stack view won't let you dynamically expend the textView as you enter more and more data so I think manipulating the hugging and resistance properties can give you a solution close to the required.
This can be achieved by creating a container view for the image view and the text label and then centering the container view in the original parent view.

How to simulate conditional hugging priorities in AutoLayout?

I have a UIView that contains a multiline UILabel and a UIImageView. The imageView is a square that is a specific height and width and is centered vertically in the cell, while the label is constrained to the top of the view. Here's a little illustration:
I want to be able to make the parent view expandable based on either the UILabel or the UIImageView, based on which one has the bigger height. How would I go about doing this (in Storyboard)?
You would set a bottom and top constraint for both the label and image view at a high priority. Set the constant equal to the amount of padding you'd want.
Then, on the parent view, you'll want to add a height constraint with a lower priority than the top/bottom constraints of the label and image.
That way, you guarantee padding between the label/image and their superview, which will force the superview to expand its height since its priority is lower than that of the padding.

Resources