I have a UIStackView
,inside a UIScrollView
to show dynamically added subviews horizontally arranged. Current solution will start displaying items from the left, I would like to start distributing items from the centre without changing the width and height of the subviews. How do I do that? Im also open to solutions which doesn't use UIStackView
as well. That way I could support devices < iOS9.
(Current)
(Expected)
Short answer :
ScrollView constraints
Leading >= 0, Trailing >= 0, Top >= 0, Bottom >= 0
Center X and Center Y
StackView constraints
Leading = 0, Trailing = 0, Top = 0, Bottom = 0
StackView width = ScrollView width (priority low :250)
StackView height = ScrollView height
Long answer
Firstly, your structure is good, we have :
UIScrollView
UIStackView (horizontal)
Subviews
So, to reach the goal we should :
UIScrollView
UIScrollView
equal to the intrinsic content
size of the UIStackView
Here is how to do it :
Step 1: Center the frame of the UIScrollView
CenterY
and CenterX
constraints used to center the frame of the UIScrollView
Leading Space
>= 0, Trailling Space
>= 0, Top Space
>= 0, Bottom Spac
e >= 0 are used to prevent the frame of the UIScrollView to exceed the frame of the parent view
I used placeholder intrinsic size to don't show errors related to the contentSize of the UIScrollView
(because we don't have yet the subviews so the contentSize).
Now, the frame of our UIScrollView
is Ok, go to the next step :)
Step 2: add the horizontal UIStackView
PS. Any change in the frame of the UIStackView, change the contentSize of the UIScrollView
Step 3: add subviews
Because we use Fill Distribution
in the UIStackView
all subviews
must have a intrinsic content size (or height and width constraints (not preferred)).
For example, if we use Fill Equally
, only one subview
with intrinsic content size (or height and width constraints (not preferred)) sufficient, the other subviews size will be equal to this one.
For Example: I will add 3 labels (please remove the placeholder intrinsic size of the UIScrollView
)
It works !! no, no, not yet try to add fourth and five labels :)
Why ?
To understand we will calculate the frame of each element of the view with two examples :
The parent view size : 200, 200 The first label intrinsic content size : 120, 50 the second label intrinsic content size : 150, 50
First example (only the first label in the UIStackView
)
UIStackView
intrinsic content size = 120, 50UIScrollView
contentSize = 120, 50 UIScrollView
frame = 40, 75, 120, 50 All frames are OK
Second example (with the two labels)
UIScrollView
frame = 0, 0, 200, 50 UIScrollView
contentSize = 200, 50 UIStackView
intrinsic content size = 200, 50 So, the UIStackView
can't show correctly the two labels (because the width of UIStackView
lower than the two labels width), and we don't have the scroll because, the UIStackView
constraint width is equal to UIScrollView width's.
It works when the UIStackView
intrinsic content size is lower than the max UIScrollView
frame.
To Fix that, we change the priority of the width constraint to a value lower than the UIStackView
intrinsic content size priority value, and all works fine :)
Hope that helps.