How to define proper constraints for scrollview to work effectively - ios

My storyboard layout is simple as shown in the attached image. I am assuming that the body text could span from a single line to multiple lines. Considering the case of multiple lines, I expect the content to scroll so that the button can be clickable. However, with the constraints I have set, the scroll view does not scroll. What constraints should I change in order to get the desired effect? Thank you.

The key to using UIScrollView: You must have a "chain" of constraints to allow auto-layout to determine the .contentSize of the scroll view.
Step-by-step (I like to set background colors during layout, to make it easy to see the bounds / frames of the elements)...
Add a UIScrollView (Cantaloupe color) to your view controller, and constrain it by 20-pts all the way around (just so we can see where it is):
Add a UIView as your ContentView (Strawberry color) as a subview of the scroll view. Give it equal Height and Width constraints to the scroll view, and top / leading / trailing / bottom constraints all set to 0:
Add your Title UILabel (Yellow background). Constrain it Top: 30 / Leading: 50 / Trailing: 50. Add your Body UILabel (Yellow background). Constrain its Top to the Title label's Bottom (I used 200), and Leading: 10 / Trailing: 10. Add your UIButton (Light Gray background). Constrain its Top to the Body label's Bottom (I used 50), and Leading: 50 / Trailing: 50:
So far, nothing special, and you won't get any scrolling...
Next, add another Bottom constraint of 0 to the ContentView, and change the first Bottom constraint to be >= 0 and set the Priority of the new Bottom constraint to 250:
This will (after a couple more steps) allow ContentView to expand / collapse based on its content, as well as control the .contentSize of the scroll view.
The next step is to tell the Button to determine the Height of the ContentView - so add a Bottom constraint of 30 to the button (which will be equal to the Top: 30 we gave to the title label):
Of course, that's not quite what we want. It stretches the height of the button because we set a height constraint on the ContentView. So... delete the height constraint from the ContentView:
The result is that the ContentView height will shrink to fit 30-pts below the bottom of the button.
If you add some more text to the Body label (assuming it is set to Number of Lines; 0), you will see the Body label expand and "push down" the button, which in turn "pushes down" the bottom of ContentView:
If you add enough text to Body to push the button below the bottom of the scroll view - either in IB or via code - it will now scroll vertically.
Hope that's clear... please ask if you have any questions.

In order to get your constraints proper for scroll view there are certain way you have to follow.please find below steps.
1.Add scroll view to super view.After adding scroll view add subview to scroll view as shown in the image.
This view will have same x,y co ordinate as well as same height and width as of scroll view.
Please refer below image to understand hierarchy.
You will find the red warning because we have not yet added any constraints.
Now its time to apply some constraints.
Refer image bellow and add top, bottom, leading and trailing to scroll view.
Now add same top, bottom, leading and trailing to scroll view’s child view. Refer below image.
Now is a real trick.Hold both scroll and view in one and give both of them equal width,equal height constraints as shown in below image.
Your constraints are almost set.now depending upon your contains stretch the view.
When you increase the view’s height after that click on update constraints.
The last thing you need to do is change scroll view’s bottom constraints value.It will be some negative value.Replace it with 0.follow below screenshot
Please update if it works

Related

"Scrollable content size is ambiguous to "ScrollView"

I am a little stuck with the ScrollView in Swift and Xcode 11.
My goal is a simple ScrollView with Labels and Textfields. Each label describes what the textfields are used for. For example there is a Label "Name", so you have to enter your name in the textfield under the label. For this I need a ScrollView because the App I am coding requires a few more information. When I am trying to put this in a ScrollView I am constantly getting the following error:
"Scrollable content size is ambiguous to ScrollView".
In order to find help I searched the internet and for example found this question: UIScrollView Scrollable Content Size Ambiguity.
Unfortunatly it didn't solve my problem. So I decided to create a small test project with the following:
- I created a project as a Single View Application
- I added a ScrollView to the ViewController
- I clicked on the constrains-Button at the canvas
- I selected 0,0,0,0 for leading/top/trailing/bottom.
- I clicked on "add 4 Constrains
- Now the warning appeared. Also the ScrollView was not resized to the ViewController... I don't know why.
- I added a UIView inside the ScrollView and set the same constrains, this time according to the ScrollView.
- The UIView (Content view) got an extra constrain: Equal width with the root view
- Now I added two Buttons, one with the text "Hello" and the other one with "world" on it.
- The "hello"-Button received the following constrains: 16 to leading, trailing and top, as well as a fixed height of 30.
- The "world"-Button got the following constrains: 16 to bottom, leading, trailing and over a 1000 so you can scroll through the ScrollView.
After all these steps the error is still there and if I run the app the "hello"-Button is at the top of the screen and the "world"-Button is at the bottom and I can't scroll.
Can maybe anyone help me fixing this issue. I am looking forward to every answer! Thank you for your help in advance!
Here is the trick that worked for me:
Add a ScrollView to your wished ViewController.
Select it in the Outline and open its size inspector.
Uncheck there the option "Content Layout Guides".
Now set leading/top/trailing and bottom constrains to 0 of the ScrollView.
Add in a UIView and constrain its leading/top/trailing and bottom also to 0.
Add an equal width constrain to the UIView. (The width needs to be equal the width of the view from the ViewController, with this way you are disabeling horizontal scrolling).
The warning will disappear if every element inside the UIView is chained vertically. This means, that the top element has a constrain to the top of the view and to the element under it and so on. The last element needs a constrain to the bottom of the view.
If you followed this steps you should be fine with ScrollViews. This way you also can add as many content as you want to the bottom and the ScrollView will extend dynamically.
I hope I explained it well enough.
It sounds like you're on the right track, and really close.
The first part is absolutely right - you add your ScrollView and pin it to zero for the top, trailing, bottom, and leading constraints. That makes it take up your whole screen.
Then you drop a plain old UIView into the ScrollView, and pin its top, trailing, bottom, and leading constraints to the ScrollView (all as zero again). Then you set the UIView to have an equal width to the ScrollView. The last thing is to set the UIView's height as equal to the ScrollView's height, but you change one thing: you set the priority of this constraint to be low (250). That's basically what allows the UIView to exceed the size of the ScrollView, so you can then scroll.
All you do then is add your buttons, etc inside the UIView, so you place them in relation to it rather than the ScrollView, and pin or align them as you wish.
Add a total of 9 constraints
1 - 4: ScrollView to Superview (leading, top, trailing & bottom)
5 - 8: Content view to Content Layout guide (leading, top, trailing & bottom)
9: Content view Width equals width to Frame Layout Guide.
But when you do this (right-click drag and drop) 7 & 8 constrains (trailing & bottom constrains to Content Layout guide) will have constant values. (check image below).
Just make them zero. Now it worked.

Fill Vertical Space of UIScrollView

I'm working on a layout that has a UIScrollView, and what I want to do is have a label pinned to the top of the scrollable area, and a button pinned to the bottom, with a minimum amount of space between the two.
The idea is that if the label grows in height the button will be pushed downwards so that the user has to scroll down to get to it. However, if the label is short, the space between it and the button should grow so that the button stays at the bottom of the scrollable area.
Below is a rough mockup of what I mean:
NOTE: The second shot is after scrolling all the way to the bottom of the UIScrollView, I want the button to be hidden when scrolled to the top; i.e- simply placing the button below the scroll view is not an option.
You can achieve the desired result with autolayout only, without having to change constraints via code. The steps are as follows:
1) Create your scrollview and pin it to all sides:
2) Add a view, which will act as content view, to the scrollview and pin it as well:
3) Your label and button will be added to this contentView, but before we can do this, we must add 2 additional constraints to the contentView. Create constraints for the width and height of the contentView to be equal as the SafeArea. You should set the priority of the equal heights constraint to low (250):
4) Add your label and button inside the contentView. The label pinned to the top and the button pinned to the bottom;
5) Lastly, add a vertical spacing constraint between the label and the button. Set the desired minimum amount of space between the button and the label as this constraint's constant, for instance 8, and set the relation to be Greater Than or Equal:
With this, if the label grows in height, the button will be pushed downwards so that the user has to scroll down to get to it.

UIScroll with Navbar and Tabbar and container view with dynamic height

I am trying to layout my detail screen in IB with a tab bar and nav bar. However, there are three issues with the display when the simulator runs:
There is a gap at the top of the screen,
The dynamic label content extends past their container views at the bottom
The scrollbar doesn't reach to the bottom of the content.
Screenshot of simulator displaying the issues described
My question is how should I set my constraints so that the above issues are resolved?
I have a lot of constraints and I'm not sure where I'm going wrong or what would be the best way to present the steps I've taken so far. But here is a screenshot of my constraints and here is a summary of the constraints/steps I've taken so far:
Main view contains just one child: The Scroll View. Scroll view is pinned to top, leading, trailing of main view and bottom is pinned to the Bottom Layout Guide.top,
Scroll view has just 1 child subview ("Content View"). Content View is pinned to top + 64, leading, trailing, bottoms to Super View, and it has equal heights and widths to the Main view.
The content view has an image, Recipe Title subview, and Shadow Background subview. The Shadow Background subview contains another subview with some labels of dynamic height. I have constraints to pin the leading and trailing sides of these containers to the superview. And I have top, bottom, leading, trailing constraints to pin these subviews to the superview and/or eachother so that there is a chain from top to bottom.
The Shadow Background View contains the labels of dynamic height. The labels also have pin constraints of top, leading, trailing and bottom with the exception of the last label which does not have the bottom constraint.
I do not have any code that updates the layout-- everything so far is in the IB.
Any help is GREATLY appreciated!
Hallelujah! After spending a full frustrating week, I've finally found the magical formula in IB for laying out a scroll view + navbar + tab bar + dynamic label height.
My original problems were caused by the following mistakes:
Adjust Scroll View Insets should have been set to false
Content View Top and Bottom pins were incorrect
Missing some heights on some of the subviews
Last subview wasn't pinned correctly on the bottom
Maybe not all these steps are required and maybe this isn't the most perfect solution, but this is what worked for me. Here is a diagram of the solution for those that prefer pictures.
Main View -> Attributes Inspector -> Uncheck the Adjust Scroll View Insets checkbox.
Add Scroll View. This is the only child of the Main View. Pin Top, Leading, Trailing Space to the Main View. Pin the Bottom to the Bottom Layout Guide.
Add one subview (name it "Content View"). This is the only child of the Scroll View. Pin Top, Leading, Trailing Space to Scroll View. Pin Bottom to Scroll view with a constant of -49 to account for the tab bar. Also set its Height and Width to be equal to the Main View.
Add a child subview to the Content View. Top is pinned to Superview with a constant of 62 to account for the Nav Bar. Leading and Trailing is pinned to the Superview. The view also needs a Height-- give it either a fixed value or a minimum value if it is dynamic content (ex: Height >- 20). You may also need to give the Height constraints a lower priority such as 250.
Continue adding sibling subviews as needed. Pin the tops to the previous sibling subview. Pin the Leading and Trailing to the Superview. The last sibling subview should be pinned to the Superview. Each subview needs a height. There needs to be one continuous chain of constraints (Top & Bottom pins, Height) from the top subview through to the bottom in order to avoid that "Scroll View has ambiguous scrollable content height" warning and have the scroll work correctly.
Do a happy dance.
Hope this helps someone else.
Make the content view's top constraint have a constant of 0, not 64. For the label extending beyond the bottom of the container view, you'll have to post more information about the layout for us to help.

UIScrollView with Content View

I've been trying to create a UIScrollView for user registry but with no success. I'm using auto layout and all of the fields that go inside the scroll view are static. Because of the usual ambiguous height issue, I've added a UIView inside the scroll view, set the constraints to the margins of the scroll view and centered aligned it. After that I added all of the fields inside that Content View, in the storyboard.
The content fields have their constraints setup as you would expect, but when I get to the lowest field and set the bottom constraint to the bottom of the Content View then everything breaks.
I'm asked by Xcode to set the priority of some views, and when I do as is says, the Content View size stays the same and the views are shrunken.
I tried not to put the last bottom constraint and resize the Content View by code but the height is not resized as is should.
I'm looking for a good solution to do this in storyboards and auto layout.
Update: I added a bottom constraint with a low priority, but the content scroll view is not expanding to show all of the fields.
Add&Set ScrollView(UIScrollView)
Add&Set ContentView(UIView) with subviews
! Set ContentView Width equal to View Width
Set all subviews constraints
View1 should be tied to the top of the ContentView
View4 should be tied to the bottom of the ContentView
All SubView (View1, View2, View3, View4 ...) must have a height and distance between each other
P.s. In your case, if iOS > 9.0 you can replace ContentView with UIStackView
You are using auto layout so the size of the content view is determined by constraints. Follow the below steps to provide proper constraints:
Drag the Scroll View inside main view and provide constraints Top, Bottom, Leading and Trailing in align with Super View (Main View) as
per screenshot.
Take View which will contain your content and drag inside Scroll View. and provide the constraints Center X, Center Y, Top, Bottom,
Leading and Trailing in align with Scroll View as per screenshot
Put all the element inside content view which is a subview of scroll view and provide Top constraint relative to the element above
it, to make equal space between the elements (eg. label, button etc.)
(Make sure you provide required constraint for X-position)
Last element is "Register Account" button make sure you provide the Top Constraint relative to country and Bottom constraint relative to
superview (content view) and change the priority for Top or Bottom
constraint as per screenshot, otherwise it gives error.

Swift - UIScrollView Scrolls Partially

My problem is slightly different from other's 'Swift UIScrollView' problems when using auto layout:
Problem:
Unlike others, when I run my app, it scrolls. My problem is that the scroll cuts off the bottom 20-30% of the content. I can pull to see the buttons did build and are down there, but when I let it go the scroll snaps back to a false bottom which cuts off my content!!! I've been trying, for days, to fix it to scroll the entire height but it continues to cut off!!
Description of app:
I used auto layout to layout 6 buttons and labels. Each button a rectangular image, with a label directly beneath it. (sorry, the site won't let me post pictures!)
I have my views arranged like this:
MainView > ScrollView > ContentView > Buttons & Labels
I have my contentView pinned to my ScrollView and my ScrollView pinned to my MainView. My buttons and labels all have constraints that are building correctly, to create a list that looks like:
Rectangular button
Label beneath it
Spacing
Rectangular button
Label beneath it
Spacing
Etc.
Can anyone tell me why I can't scroll the full length of the view?
Your description of how your items are constrained is vague, so I'm going to list all of the constraints you need to make this work. Then you can compare what you have to what you need and adjust accordingly.
Your ScrollView should be pinned on all 4 sides to the MainView. (This isn't absolutely necessary. You can constrain your ScrollView however you want, but make sure it can grow as the device and/or orientation changes).
Your ContentView should be pinned on all 4 sides to the ScrollView with offsets of 0.
Since you want your ScrollView to scroll vertically only, constrain the width of the ContentView to the width of the ScrollView using an Equal Widths constraint. To do this, in the Document Outline view, Control-drag from your ContentView to your ScrollView and select Equal Widths from the pop up.
The height of ContentView will be set by the sum total height of everything in it. In order for this to work, your topmost button needs to be pinned to the top of the ContentView, all of your buttons and labels should be pinned to their nearest neighbors, and the bottommost label should be pinned to the bottom of the ContentView. In addition, all of your buttons and labels should have constraints for their widths and heights. I would suggest setting an explicit width constraint and explicit height constraint for your buttons and centering them horizontally in the ContentView. For your labels, set an explicit height constraint and pin the left and right edges to the ContentView.
If you have these constraints and no other ones, your ContentView will be properly sized.
Using contentView, like you said, usually fixes the issue. So chances are you need to take a second look at your contraints. Maybe try this solution in a clean/new project to see that it works. (it does work). My guess would be that some of your constraints conflict each other.
Otherwise I think it would be a good idea to setContentSize of your scrollView in your viewDidLoad.
Another hack would be to place 2 UI objects with their alphas set to zero on the right top corner and left bottom corner. This would hint scrollView to set its contentSize.

Resources