32

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.

Curent

(Current)

Expected

(Expected)

HamzaGhazouani
  • 6,464
  • 6
  • 33
  • 40
Ajith
  • 1,457
  • 3
  • 16
  • 29
  • Are you working in Swift or Objective C? – Jagat Dave Feb 16 '16 at 12:11
  • Im working on Swift 2.0 – Ajith Feb 16 '16 at 12:12
  • Are you going to cap the amount of subViews at 3 inside the stackView? – Adrian Feb 16 '16 at 12:19
  • Subviews inside the UIStackView would come dynamic, could be n numbers. That's why I need the UIScrollView here. – Ajith Feb 16 '16 at 12:22
  • I have 4 buttons, all 50 x 50. I set the Stack View to `Equal Spacing` and constrained the height and width of the buttons. The first and last button were on the edge of the stack view. To add equal spacing to the start and end of the stack view, I just added two 1x50 labels as the first and last elements in the stack view. Hacky, but works ;) – Patrick Jan 06 '17 at 16:34

1 Answers1

36

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 :

  • Center the UIScrollView
  • Set the contentSize of the UIScrollView equal to the intrinsic content size of the UIStackView

Here is how to do it :

Step 1: Center the frame of the UIScrollView

enter image description here

CenterY and CenterX constraints used to center the frame of the UIScrollView

Leading Space >= 0, Trailling Space >= 0, Top Space >= 0, Bottom Space >= 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

enter image description here

  • Top, Bottom, Leading, Trailing constraints are used to fix UIStackView frame
  • Equal Height and equal width used to calculate the contentSize of the UIScrollView

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)

enter image description here

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, 50
  • UIScrollView 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 :)

enter image description here

Hope that helps.

Code source

HamzaGhazouani
  • 6,464
  • 6
  • 33
  • 40