22

Here is a quick question about something which works, but could be written much better. I have a UIScrollView and a list of objects laid out inside, one under the other. Everything is done during viewDidLoad() and the placement of the objects uses Auto Layout. Here is what I do to set the contentSize height of the UIScrollView to its appropriate value.

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    globalView.contentSize = CGSize(width: globalView.frame.width,
                                    height: globalView.frame.height * 1.59)
}

It works, but the arbitrary value 1.59 has obviously been decided by me trying a few possible values. What is the proper way to compute the contentSize in such a case? I am doing everything programmatically. Searching the net didn't lead me to any simple and clear answer, so eventhough it may be a somewhat duplicate question I decided to reformulate it.

4
  • You can find lots of answers on the same topic about setting the constraint to the UIScrollView. If you set the constraints correctly to the all component inside UIScrollView then you would not need to set the constant size programatically. See this link explaining the same - stackoverflow.com/questions/35624873/… Commented Aug 16, 2016 at 7:25
  • True but most (all) of them presume you are using storyboard :)
    – Michel
    Commented Aug 19, 2016 at 8:52
  • If you are adding subviews from code, you can probably just go to Size Inspector and select Ambiguity->Never Verify at the bottom. Commented Nov 4, 2018 at 13:57
  • 1
    @Paweł Pela : This was a while ago, but anyway see my favorite solution, if you are interested. Look for AutoLayScroll in one of the answers below.
    – Michel
    Commented Nov 5, 2018 at 6:28

6 Answers 6

86

Giving content size programatically is not good way. The below solution which will work using autolayout, dont need to set content size at all. It will calculate as per how many UI fields added to view.

Step 1 :

Add Scrollview to view in storyboard and add leading, trailing, top and bottom constraints (All values are zero).

Step 2 :

Don't add directly views which you need on directly scrollview, First add one view to scrollview (that will be our content view for all UI elements). Add below constraints to this view.

1) Leading, trailing, top and bottom constraints (All values are zero).

2) Add equal height, equal width to Main view (i.e. which contains scrollview). For equal height set priority to low. (This is the important step for setting content size).

3) Height of this content view will be according to the number of views added to the view. let say if you added last view is one label and his Y position is 420 and height is 20 then your content view will be 440.

Step 3 : Add constraints to all of views which you added within content view as per your requirement.

For reference :

enter image description here

enter image description here

I hope this will definitely help you.

14
  • 2
    That sounds interesting, but since I am not using storyboard. I need to see how to apply these explanations when working programmatically. The first question being, in step 2 how do you set a view as your content view?
    – Michel
    Commented Aug 19, 2016 at 6:43
  • Its normal view, i've added to scrollview (treating as contentView). Then on that view I'm placing all UI elements.
    – Shrikant K
    Commented Aug 19, 2016 at 8:24
  • 1
    I finally did it, it works programmatically, the way I want. But I am still not sure how this contentview stuff works. What will happen if I add say 3 subviews with different height? Which one will be considered as content view?
    – Michel
    Commented Aug 19, 2016 at 8:51
  • Great ! If you want add 3 views into scrollview then what i do is will take one view (which will contain the 3 views) and add it to the scrollview. Thats it. (I mentioned in above example)
    – Shrikant K
    Commented Aug 19, 2016 at 10:55
  • 1
    This is the best answer I've seen until now regarding this topic! But there's another case: if the sum of my views' height + margins is less than the space available, the equal height constraint wins, so, when the keyboard pops up and I set the inset equal to keyboard's height, I can scroll down way too much. How can this situation be addressed? Commented May 3, 2017 at 15:25
4

Though I finally got to solve this issue and make things work. I noticed that all I could find on the net as related answers was directed to storyboard users. But nothing to do it programmatically that I could put my hands on.

So I decided to make an extremely simple demo app to show how this can be achieved. I hope it will be useful to someone at some point.

Here is the address to get it on GitHub: https://github.com/zaxonus/AutoLayScroll

3
  • This example was created in order to show how to do everything programmatically. If you want to use XIB (storyboard) there many example on the net. The first one being the one mentioned above, in the accepted answer.
    – Michel
    Commented Aug 27, 2016 at 7:50
  • This is excellent. Several days of trying things and this is the only one that worked. Note, works in Swift 3, given variable renaming. Thank you sir.
    – mike
    Commented Nov 9, 2016 at 3:48
  • I think you can use a UIStackView pinned in all directions to the UIScrollView instead of that. Commented Feb 22, 2019 at 14:42
4

All you need to do is to make sure that the first subview's top anchor is constrained to the scrollView's top anchor and the last subview's bottom anchor is constrained to the scrollView's bottom anchor

3

I also used constraints programmatically to dynamically modify UIScrollView's contentSize . I pretty much followed Shrikant's instructions but for step 2.1, I set centerX to the scrollViews.centerX rather than set the leading and trailing margins (No idea why the former works, while the latter doesn't). Then as a last step, after adding the final subview, I did the following:

contentView.layoutIfNeeded() //set a frame based on constraints
scrollView.contentSize = CGSize(width: contentView.frame.width, height: contentView.frame.height)

Hope this helps somebody in the future.

1

If you don't want to use storyboards, and just use constraints, you can follow the guide here

https://redflowerinc.com/implement-uiscrollview-using-constraints-no-need-to-use-content-size/

Use can use bottom anchor and pin your views to the scroll view. By using constraints you don't need to use content size.

1
  • That is exactly what you need to implement ScrollView using constraints! The key piece of code is "lastLabel.bottomAnchor.constraint(equalTo: scrollview.bottomAnchor).isActive = true"
    – Vette
    Commented Apr 23, 2020 at 16:25
0

This is the official guide from Apple, and works for me.

https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithScrollViews.html

To support scroll views, the system interprets constraints differently, depending on where the constraints are located.

  • Any constraints between the scroll view and objects outside the scroll view attach to the scroll view’s frame, just as with any other view.
  • For constraints between the scroll view and its content, the behavior varies depending on the attributes being constrained:
    • Constraints between the edges or margins of the scroll view and its content attach to the scroll view’s content area.
    • Constraints between the height, width, or centers attach to the scroll view’s frame.
  • You can also use constraints between the scroll view’s content and objects outside the scroll view to provide a fixed position for the scroll view’s content, making that content appear to float over the scroll view.

For most common layout tasks, the logic becomes far easier if you use a dummy view or layout group to contain the scroll view’s content. When working in Interface Builder, the general approach is shown below:

  1. Add the scroll view to the scene.
  2. Draw constraints to define the scroll view’s size and position, as normal.
  3. Add a view to the scroll view. Set the view’s Xcode specific label to Content View.
  4. Pin the content view’s top, bottom, leading, and trailing edges to the scroll view’s corresponding edges. The content view now defines the scroll view’s content area.

The content view does not have a fixed size at this point. It can stretch and grow to fit any views and controls you place inside it.

  1. (Optional) To disable horizontal scrolling, set the content view’s width equal to the scroll view’s width. The content view now fills the scroll view horizontally.
  2. (Optional) To disable vertical scrolling, set the content view’s height equal to the scroll view’s height. The content view now fills the scroll view horizontally.
  3. Lay out the scroll view’s content inside the content view. Use constraints to position the content inside the content view as normal.

IMPORTANT

Your layout must fully define the size of the content view (except where defined in steps 5 and 6). To set the height based on the intrinsic size of your content, you must have an unbroken chain of constraints and views stretching from the content view’s top edge to its bottom edge. Similarly, to set the width, you must have an unbroken chain of constraints and views from the content view’s leading edge to its trailing edge.

If your content does not have an intrinsic content size, you must add the appropriate size constraints, either to the content view or to the content.

When the content view is taller than the scroll view, the scroll view enables vertical scrolling. When the content view is wider than the scroll view, the scroll view enables horizontal scrolling. Otherwise, scrolling is disabled by default.

Not the answer you're looking for? Browse other questions tagged or ask your own question.