UIStackView - Distribute views evenly from the centre - ios

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 :
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
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
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)
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 :)
Hope that helps.
Code source

Related

XCode: Scrollview not scrolling

so I have added scrollview to my screen.
However, I'm unable to scroll up and down.
My scrollview width and size are 414 and 784 respectively while my content size is scrollView.contentSize = CGSize(width: self.view.frame.size.width, height: 800)
Why can't I scroll up and down?
How do I fix this?
You can add a UIView (call it content view) inside your scrollView with top, botttom, left, right constraints to zero. And give height constraint = 800 for this view.
This should work!
Basically, you need to add some content to the scrollView. If the height of contents becomes greater than the scrollView height, the scrollView becomes scrollable.

My scrollView does not wants to scroll

This is my setup:
I do not know what I am doing wrong. The image view is bigger than the size of the view and of the scroll view. The constrains are set al followed:
Scroll view: equal heights to View * 0,5, equal width to View, center Y and X to View.
View (inside Scroll view): pinned all zero's inside Scroll view, equal heights and width. I also tried instead of equal heights and widths to center X and Y inside Scroll view, but it won't scroll.
How can I let the Scroll view scroll? Thank you.
Add a leading, trailing and top constraint and equal height of UIScrollView to superview with 0.5 multiplier. Now to your contentView (the UIScrollView subview), add a leading, trailing , top and bottom constraint. Also add equal height and width to UIScrollView. Set the height to a priority of 250. Add constraints for UIImageView inside this contentView.
Since the contentView will have a fixed height of low priority equal to the UIScrollView height. This fixed height constraint will break once the UIImageView total height(based on the constraints you add) will get larger than the UIScrollView height and the content will become scrollable. So at the very least you will always have a view half the screen size and become scrollable once the content becomes too large vertically.
You need to give contentSize to scrollview.
ScrollView.contentSize = CGSize(width: 1000, height: 500)
Which constrains have you given to imageview?
set constraint of imageview:
Trailing ,leading,top,bottom - 0 and also give height constraint.

How Can I have multiple widths and heights of subviews in a UIStackView

Say for example I want to add 3 buttons to a stack view.
Button1 is 200 * 200 (1)
Button2 is 150 * 150 (0.75)
Button3 is 100 * 100 (0.5)
How can I accomplish this in interface builder?
You can do it in the storyboard using stackview
Add a stackview with Alignment Center and Distribution Fill.
Constraints on stackview is center horizontally and center vertically.
Now add three buttons.
Constraints on button1 is height and width = 200
Constraints on button2 is height and width = 150
Constraints on button3 is height and width = 100
The set up is shown in fig:
You are having conflicts because of Alignment Property which in your case it fill. make is center and its done.
Also we can add spacing between buttons. If we set Distribution Equal Spacing and add height constraint to stackview with height more than height of buttons.
This is vertical stackview. You can test the same with horizontal stackview.
Hope it will work for you.
For those who are looking a solution for UIView, Here it is,
UIStackView ensures that its arranged subviews maintain the same proportion to each another as your layout grows and shrinks. However, unlike the other distribution options, views that are proportional must have an intrinsic content size.

UIView in UIScrollView does not adjust width

I have been searching through stackOverflow and whatever google proposes but I wasn't able to get it to work. I am intending to draw a simple 2D Graph in a scrollview, the distance between my datapoints is kStepX and I want the scrollview to be at least the width of the screen, if I have more datapoints it should scroll but no more than 100 points.
I think I have a problem with my Autolayout and sizing the contentWidth, so here is what I have done so far:
I added a UIScrollView with the following constraints:
Leading Space to Superview =0
Top space to superview =0
Height = width of superview
Width = width of superview
I then added a UIView (called GraphView) as a Child with the following constraints:
zero space to all 4 bounds of scrollview
center X and center Y to scrollview
in my GraphViewController I set the contenSize as:
historyScrollView.contentSize = CGSizeMake(MAX(SCREEN_WIDTH,sizeof(data)*kStepX), kGraphHeight);
but it does not scroll!
if I set a fix width in the storyboard the scrollview scrolls further than I have data..
What am I doing wrong?
You should not be setting the contentSize of the scrollView when using auto layout. That should be calculated from your constraints if they are setup correctly.
What you are missing is to set a width and height constraints on the view inside the scrollView. A scrollView determines it's contentSize based on the size the subviews have. Since an UIView does not have an intrinsic size, you will need to add width and height constraints to it, then update it when you need it with the right value
Something like this should work:
innerViewWidthConstraint.constant = MAX(SCREEN_WIDTH,sizeof(data)*kStepX)
innerViewHeightConstraint.constant = kGraphHeight
// You might need to layout the views too
[historyScrollView layoutIfNeeded]
Hope this helps! Good luck :)
Take ScrollView in side in your Main View and give Top,Bottom,Leading,Trailing,Center X and Center Y.
after take GraphView inside you scrollview and also set constrain of GraphView.
set height of GraphView
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *HeightGraphView;
whenEver you set your HeightGraphView then automatically set ScrollView contentSize.
see my demo its working for Vertically scrolling, apply same for Horizontal scrolling.
Demo Vertical Scrolling

minimum x-value of the superview’s bounds?

What does this expression "minimum x-value of the superview’s bounds" mean in the Apple Documentation regarding Autolayout in a UIScrollView?
...some notes regarding Auto Layout support for UIScrollView:
In general, Auto Layout considers the top, left, bottom, and right edges of a view to be the visible edges. That is, if you pin a view to
the left edge of its superview, you’re really pinning it to the
minimum x-value of the superview’s bounds. Changing the bounds origin
of the superview does not change the position of the view.
The UIScrollView class scrolls its content by changing the origin of its bounds. To make this work with Auto Layout, the top, left,
bottom, and right edges within a scroll view now mean the edges of its
content view.
1) As I know by default bounds of a view is (0, 0, width, height). So x is 0.
2) How could a view have more x values to take minimum on it?
The Autolayout constraints for any view is generally in relation with the visible bounds of the view but when it comes to a UIScrollView, the Autolayout constraints are in relation with the scrollView's contentView.
Obviously, because a scrollView is, well... supposed to scroll.
Suppose a scrollView has a contentSize of something like:
myScrollView.frame = CGRectMake(0, 0, 100, 100);
myScrollView.contentSize = CGSizeMake(1000, 1000);
now...
Say, in this scrollView you have a subView, something like:
UILabel *lblTest = [UILabel alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
[lblTest setText:#"Where am I?"];
[myScrollView addSubView:lblTest];
Before Scrolling:
myScrollView.contentOffset.y will be 0
myScrollView.bounds.y will be 0
myScrollView.frame.origin.y will be 0
lblTest.frame.origin.y will be 0 (and will be visible)
After Scrolling (to bottom):
myScrollView.contentOffset.y will change to 900
myScrollView.bounds.y will also be 900
myScrollView.frame.origin.y will still be 0
lblTest.frame.origin.y will be -900 (and no longer visible)
This is what the Apple Documentation means by:
The UIScrollView class scrolls its content by changing the origin of
its bounds.
Now...
If Autolayout was in relation with the visible bounds of UIScrollView then no matter how much you scrolled, the UILabel would not scroll up.
but...
Since Autolayout in a UIScrollView goes in relation with the scrollView's contentSize, the Autolayout contraints work within it with relation to the scrolling functionality.
So...
When you use Autolayout for subViews in a scrollView, it will be relational to the scrollView's actual size and not the visible size.
apple docs says ask: The UIScrollView class scrolls its content by changing the origin of its bounds.
1) so scrolling can change bounds.x, and than x will be not 0
bounds property is usually (0, 0, width, height), but not always
2) the minimum means: the top, left, bottom, and right edges within a scroll view now mean the edges of its content view.

Resources