How to assign constraints to a Scrollview which covers part of the screen only?

I want to use a Scrollview on the middle 2/3 of the screen so I did the following:


1) I added a BaseView UIView to the middle 2/3 of my MainViewController and set the constraints to the following:


leading =0

trailing =0

top to safe area = 150

bottom to safe area = 150


2) I added a MyScrollView inside the BaseView and set the constraints as follows:

Note: All MyScrollview constraints remain RED after setting these constraints :>(


leading = 0

trailing = 0

top = 0

bottom = 0


3) I added a MyDataView UIView object inside the MyScrollView . The MyDataView starts at (0,0) and has a size of 500x500. I tried setting constraints on MyDataView to the following values but the top and leading constraints remain RED, most likely because the MyScrollView constraints are RED (see 2 above)


top=0

leading=0

width=500

height=500



4) In the viewDidLoad function I added the following:


ScrollView.delegate = self

ScrollView.contentsize = MyDataView.frame.size


5) I added various objects (buttons, labels, etc.) in the MyDataView UIView and tried to set constraints on these objects but the constraints are all RED as well.


Questions:

1) I am setting up the ScrollView properly so it can be used for 2/3 of the screen area only?


2) How can I set valid constraints for the objects placed in the MyDataView and have them be a nice BLUE color?

Accepted Reply

The point is how constraints are set for the contentView.

- remove the authorising constraints

- control-drag to scrollView and set top, leading, bottom, trailing

- change values to zero for those which were not

- add constraints on contentView, for width and height


Error (red badges) disappear. There may remain some harmless yellow warnings though.

Replies

If you set constant at 150, that will not be 2/3 in all cases (e.g., landscape, or iPad).

:> You should compute this value depending on screen height


1) I am setting up the ScrollView properly so it can be used for 2/3 of the screen area only?

Do you get what you expect ? Otherwise, what do you get ?

Note: All MyScrollview constraints remain RED after setting these constraints :>(

Why do you create a BaseView and put the Scroll inside ?

- You'd probably better create the Scroll at top hierarchy level

Set its constraints

leading =0

trailing =0

top to safe area = 150

bottom to safe area = 150

- then put the BaseView inside the scrollView

Set its constraints relative to ScrollView

leading = 0

trailing = 0

top = 0

bottom = 0

- Put the objects inside BaseView

If the warning is about contentSize ambiguity, they should disappear

May have a look here: h ttp://synappse.co/blog/uiscrollview-has-ambiguous-scrollable-content-height/


2) How can I set valid constraints for the objects placed in the MyDataView and have them be a nice BLUE color?

BLUE color for what type of objects ???

To set constraint, use autolayout:

- place the object inside MyDataView

- control-drag from the object to MyDataView to set top, leading… or other


Note: These are not Swift questions. You should move your post to Interface Builder section.

>>If you set constant at 150, that will not be 2/3 in all cases (e.g., landscape, or iPad).

Yes, understood :>) I am just reporting my issue in a general fashion so I try to keep my explanation/summary to a bare minimum so I do not get into the super details of exactly what I am doing, My point is that I do not want to have a ScrollView covering the complete screen. Just assume I am using iPhone 8 in portrait mode and I only want 2/3 of the screen to be scrollable.


>>Why do you create a BaseView and put the Scroll inside ?

I believe I read a ScrollView must have all of it's constraints (ie: leading, trailing, top and bottom) set to zero(0) for it to work properly. is this true? Assuming this is true, then if I want the Scrollview to cover the complete ViewController screen then this is easy to do and does not involve me creating a BaseView UIVIew, however, since I only want the ScrollView to cover 2/3 of the screen then the only way to have ScrollView constraints all being zero(0) is if I put the ScrollView into a BaseView UIView. Basically, the BaseView UIView covers 2/3 of the screen and the ScrollView is placed inside the BaseView, thus allowing me to set all constraints of the ScrollView to 0.


Are you saying I can place a ScrollView object onto the ViewController area and set constraints so the ScrollView only covers 2/3 of the screen?


>>Do you get what you expect ? Otherwise, what do you get ?

Here is my first main issue simply described:


Issue #1

I place a BaseView UIView on to the ViewController and set the BaseView constraints so it covers 2/3 of the screen. This works nicely and all constraints are BLUE. I now place a ScrollView inside the BaseView and set the ScrollView cosntraints (ie: leading, trailing, top and bottom) to be zero(0). After this, my issue is that all of the ScrollView constraints are RED. Why are the ScrollView constraints not all BLUE at this point?


>> You'd probably better create the ScrollView at top hierarchy level (leading/trailing=0 and top/bottom=150)

>> then put the BaseView inside the ScrollView (leading/trailing/top/bottom=0)

This would mean the BaseView would be the same exact size of the ScrollView. The objects which I am dealing with cover a 500x500 UIView area, thus I need scrolling capabilities. As a result, I cannot place all of the objects into a UIView which is the same exact size as the ScrollView, as you are suggesting.


Issue #2

I am using interface builder to place UIViews and ScrollView into my storyboard. The UIView which has all of my objects (buttons, labels, etc.) has a size of 500x500 (for example). This UIView needs to go inside of the ScrollView to allow scrolling to eventually occur. I would like to set various constraints on the objects (buttons, labels, etc.) located within the UIView, however, I cannot set valid BLUE constraints on these objects until I can set valid BLUE constraints for the UIView and for the ScrollView first.

Are you saying I can place a ScrollView object onto the ViewController area and set constraints so the ScrollView only covers 2/3 of the screen?


Yes, as explained in the link I posted.


The idea to have UIView inside is to silent some (erroneous ?) IB warnings that content of ScrollView height or width are ambiguous

That may explain your #3 concern "Why are the ScrollView constraints not all BLUE at this point?"


As a result, I cannot place all of the objects into a view which is the same exact size as the ScrollView, as you are suggesting.

In some case I have a ScrollView


OK, I'll try to fully implement a case, inserting an image into the View in the ScrollView.

Well, I did it as I said. The view inside the scrollView has the size of the scrollView (could be larger, so that you can position objects beyond the scrollView limit).

The image inside this view is larger, that is not a problem.


It was a bit tricky (and honestly not really useful) to silence all IB warnings, but I managed to do it, defining constraints:

- To position the scrollView relative to safe area and specifying its witdh and height (that seem problematic to me, somehow overconstrained) ; but otherwise, I get warnings

- the view inside has the 4 constraints (left, top, bottom, right, Plus center horizontally and vertically (same comment here)

- for the image, I added 4 constraints (the struts with the little red lines): but removing them does not change anything.

- its size does not matter as I set it in code, but it must be larger that the scrollView to see the scroll


Finally, in code:


    private var imageSize = CGSize(width: 960, height: 640)

    override func viewDidLoad() {
    
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        imageView.frame.size = imageSize

        scrollView.autoresizingMask = [UIView.AutoresizingMask.flexibleWidth, UIView.AutoresizingMask.flexibleHeight]
    }

    override func viewDidLayoutSubviews() {
      
         scrollView.contentSize = self.imageSize 
    }



If you want to work on view larger that the viewController (for the content view), you can either:

- do it by hand, selecting objects in the list and setting the position or constraint without viewing it inside the viewController (not very easy)

- or use freeform for a while

- or create a xib for the view

For the latter 2, read this:

https://stackoverflow.com/questions/25682729/ios-storyboard-how-do-you-edit-a-content-view-larger-than-screen-in-a-uiscrollv

>>May be have a look here: h ttp://synappse.co/blog/uiscrollview-has-ambiguous-scrollable-content-height/

This webpage indicates setting the ScrollView and UIView to have constraints all set to "0", which will not work for me. This example shows how the UIView gets larger when more and more text is added to the TextField thus pushing the UIButton (and text) out of the visual screen area, thus allowing scrolling to be initiated. I am assuming when the ScrollView constraints are all set to "0" then they are all RED for this guy as well, but he never mentions this at all


>>If you want to work on view larger that the viewController (for the content view), you can either:

- do it by hand, selecting objects in the list and setting the position and constraint without viewing it inside the viewController

- or use freeform for a while

- or create a xib for the view

This is not a problem. I simply reposition the content UIView, as required, within the viewController so I can see all objects which were previously off the screen. I can then assign constraints and positions as desired. This is not that bad as I am not dealing with ScrollViews that often. If I was constantly dealing with ScrollViews, then this technique would eventually get annoying.



For testing, I created a brand new Xcode project and did the following:


1) I added a ScrollView with the following constraints:


leading safe area = 0

trailing safe area = 0

top to safe area = 150

bottom to safe area = 150


  • There are RED lines around the scrollview and a warning which states the "content size is ambiguous"
  • I tried adding fixed width(375) and height(367) constraints to the ScrollView to see if the RED lines would go away (based on your comment) but they did not. I then removed these width/height constraints since they did not help and really should not be required anyway. Are you saying you were able to get the RED cosntraint lines to turn to BLUE by adding width/height constraints somehow?
  • At this point, if you switch to "Landscape" mode you will see the ScrollView object does not resize itself based on the assigned constraints, which is odd. I guess then means I cannot assume the ScrollView dimensions will be properly set when my application enters Landscape mode. (Note: To resolve this issue, perhaps we need to place the ScrollView inside of a BaseUIView object, whose constraints are set as desired, and then ensure the ScrollView frame size is always equal to the BaseUIView? :>)
  • In the viewDidLoad() I have the following --> myScrollView.contentSize = myUIView.frame.size
  • The UIView is where I am planning on adding all of my buttons and labels which the user can scroll around and see.
  • I do not want to set the UIView leading, trailing, top, bottom constraints to be 0 (like you did) since I do not want the UIView to be the same size as the ScrollView. I need the UIView dimensions to be larger than the ScrollView so scrolling will be allowed. I am going to use the Auto Layout "Vary for Traits" option to set different button/label/etc. constraints depending on whether I am in Portrait or Landscape modes so I need to be able to work with the complete UIView dimensions.



Questions:

a) Is there a way to resolve the ScrollView "content size is ambiguous" warning in interface builder to make all constraint lines turn BLUE?


b) In the code, I make sure the UIView height/width are correct and also make sure the "ScrollView.contentsize" is set appropriately. In order to set valid (BLUE) constraints on the objects (buttons, labels, etc.) located within the UIVIew, I must first set valid (BLUE) constraints for the UIView itself first. As a result, I set the following constraints for the UIView:


leading = 0

top = 0

width = 700

height = 700


I then set valid (BLUE) constraints for all buttons, labels, etc. located within the UIView.


When I run the application and attempt to scroll, I see the "scrollViewDidScroll( )" UIScrollViewDelegate function IS being called repeatedly, but no actual scrolling of the objects is occurring visually. The UIButtons located in the UIView can be clicked.


I discovered if I remove the UIView four constraints (leading, top, width and height) then scrolling now works visually, but none of the UIButtons are clickable. All weird stuff.


Any idea what is going on here?

a) Is there a way to resolve the ScrollView "content size is ambiguous" warning in interface builder to make all constraint lines turn BLUE?


If this code works OK, you should just ignore the warning (that's what I do !)

Trying to clear creates real complications essentially useless IMHO.

my code does not work. I have been trying for two days. I always dread trying to work with a ScrollView since I never can get it working.


The simple issues are:


1) Put ScrollView into ViewController and assign these 4 constraints --> leading/trailing safe area=0 and top/bottom safe area = 150

  • ScrollView constraints remain RED
  • If you switch to Landscape mode then ScrollView dimensions do not change


2) Put UIView into ScrollView with these dimensions --> x=0, y=0, width=500, height=500

I cannot assign constraints to UIView since ScrollView constraints are not set


3) Put UIButton into UIView

I cannot assign constraint to UIButton since UIView constraints are not set



Due to these issues, when I use a ScrollView I eventually end up doing one of the following:


a) Never assign constraints to any objects located within the content view UIView and update object positions via the code only.

OR

b) Position the objects in the content view UIView and ensure they do not have to change positions when switching between Portrait and Landscape modes. Basically, just allow the ScrollView to scroll through the same looking objects no matter what orientation I am in.


It seems like I am going to need to choose (a) or (b) again and not really layout the objects via constraints like I want to. It just means I have to assign a name to eveery single object located in the UIView and then write more code to manually position them during an orientation change.

Fore sure, auto layout for ScrollViews is very confusing.


I looked at those 2 references:

h ttps://www.freecodecamp.org/news/how-to-use-auto-layout-with-uiscrollview-for-ios-b94b8687a4cc/

But you will see that the red dot in IB is always here, meaning there are still error messages


h ttps://useyourloaf.com/blog/scroll-view-layouts-with-interface-builder/

recommended here: https://stackoverflow.com/questions/56570660/how-to-fix-scrollable-content-size-ambiguity-in-xcode-11-ios-12-ios-13-usin


with the following remarks:


I stacked with that problem as well. Found a good guide that helped me:

h ttps://useyourloaf.com/blog/scroll-view-layouts-with-interface-builder/

Basically what you need is 9 constraints (assuming you want to scroll only vertically):

1-4: ScrollView to Superview (top, bottom, leading, trailing). Make sure to connect it to parent view and not to safe area.

5-8: Content view to Content Layout guide (top, bottom, leading, trailing).

Content view Width equals width to Frame Layout Guide.


But there may well be some bug in XCode as well.


EDITED.

Looks like the trick is to disable Content layout guide, in the Size Inspector for ScrollView (at the top of the inspector)

That effectively cleared the red outlines errors. Now, changing orientation also changes the scrollView size.

I created an ImageView inside

I ended up with:

- ScrollView, with 4 constraints to its superView

- imageView with constraints to its superView (the scrollView)


and code


class InitialViewController: UIViewController {

    @IBOutlet weak var scrollView: UIScrollView!  
    @IBOutlet weak var imageView: UIImageView!

    private var imageSize = CGSize(width: 1920, height: 1280)     // Made it very large to see scroll
   
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        imageView.frame.size = imageSize 

        scrollView.autoresizingMask = [UIView.AutoresizingMask.flexibleWidth, UIView.AutoresizingMask.flexibleHeight]
    }

    override func viewDidLayoutSubviews() {
       
        scrollView.contentSize = self.imageSize
    }

}

Did disabling Content layout guide, in the Size Inspector for ScrollView work for you ?

Hey Claude,


Sorry for the late response. Just returned back into town.


Unfortunately, I decided I spent way too much time trying to hack together a ScrollView to get it working. I resorted to plan (a) below:


a) Never assign constraints to any objects located within the content UIView and update object positions via the code only


I read through your websites and it is really going to take me some time to slowly read and re-read this material to understand what they are talking about. I bookmarked all of this material and if I ever have to deal with a ScrollView again I will re-read all of this stuff and try again. I just cannot afford to spend hours anymore on ScrollView work. Too much other stuff to tackle.

There was an easy solution it seems


" disable Content layout guide, in the Size Inspector for ScrollView (at the top of the inspector)"


You could try it, it's easy to implement.


Anyway, most important is you found a solution, you could probably close the thread now.

Claude,


Ok, I created a brand new test project.


1) I placed a Scrollview (with light blue background) into the default ViewController and set ScrollView constraints as follows:


leading = 0 to safe area

trailing = 0 to safe area

top = 150 to safe area

bottom = 150 to safe area


2) I added a ContentView UIView (with yellow background) inside the ScrollView with the folliowing values and no constraints set at this time:


X = 0

Y = 0

Width = 700

Height = 700


3) I added many buttons and labels inside the ContentView which have been positioned down the left side of the ContentView in a vertical fashion from Y=0 to Y=600 so some of the buttons/labels go outside the display viewing area.


4) Here is the code:


import UIKit

class ViewController: UIViewController
{
    @IBOutlet weak var ScrollView: UIScrollView!
    @IBOutlet weak var ContentView: UIView!
      
    override func viewDidLoad()
    {
        super.viewDidLoad()
        ScrollView.delegate = self
    }

    override func viewDidLayoutSubviews()
    {
        super.viewDidLayoutSubviews()
        print("viewDidLayoutSubviews")

        ScrollView.contentSize = ContentView.frame.size
    }
}

extension ViewController: UIScrollViewDelegate
{
    func scrollViewDidScroll(_ scrollView: UIScrollView)
    {
        print("scrolling is occurring.  You can see the ScrollView scrollbars moving as well")
    }
}



4) In the size inspector for the ScrollView, I unchecked the "Content Layout Guides" option box.


Results:


In IB...

- The constraints of the ScrollView are still RED and switching to landscape in IB does not resize the ScrollView dimensions

- Still receive the "Scrollable content size is ambiguous for Scroll View" warning


When running the application...

- The ContentView contents CAN be scrolled up/down/left/right to see the full UIView (as previously)

- Switching the landscape mode resizes the scrollview area (as previously)


Issues which still remain as before:

  • I cannot assign constraints to UIButtons/UILabels located within the ContentView since no ContentView constraints are set. Setting these constraints is the main goal of mine.
  • When I set the following 4 constraints on the ContentView, then scrolling no longer works when running the application, although you can see scrolling events are being processed and also notice the scrollbars on the ScrollView are moving around as well.


leading = 0

top = 0

width = 700

height = 700



Questions for you

1) You indicated you set ImageView constraints. What exact ImageView constraints did you set?

2) Instead of using an ImageView, can you try a UIView and place UIButtons inside the UIView and see if you can set constraints on the buttons?

Compared to your set up, creating a brand new ViewController:

- Step 1: OK

- Step 2: all red lines, and warning: (red arrow): ambiguous scrollable content

- Step 3: OK, created 8 labels down to y = 600 in ContentView

- Step 4: same


Run the code: I can scroll. All work OK

- Step 5

- turn off Content Layout Guides: lines stay red and ambiguous scrollable content

- Added constraints for the contentView : leading, trailing, and Top (0: content.top = superview.top + 0), Bottom (300: superview.bottom = content.bottom + 300), width (700 as proposed), height (700 as proposed)

=> Lines turn blue

=> red warning disappear

BTW: turn ON Content Layout Guides does not cause problem now !!!!


Run code: OK in Portrait and in Landcape (even though contentView is a bit narrow vertically.


- Step 6: turned landscape in IB: redrawn correctly


Questions for you (me)

1) You indicated you set ImageView constraints. What exact ImageView constraints did you set?

Well see question 2, I created a test case with UIView

2) Instead of using an ImageView, can you try a UIView and place UIButtons inside the UIView and see if you can set constraints on the buttons?

I placed Labels not buttons, but that does not make a difference.

To be sure, I added 8 buttons next to the labels. Still works OK.



So, the conclusion is:

Need to set constraints Top (0: content.top = superview.top + 0), Bottom (300: superview.bottom = content.bottom + 300)

>> Added constraints for the contentView : leading, trailing, and Top (0: content.top = superview.top + 0), Bottom (300: superview.bottom = content.bottom + 300), width (700 as proposed), height (700 as proposed)

>> => Lines turn blue

>> => red warning disappear


While "Content Layout Guides" is off I then click on the ContentView to set constraints. When using Auto Layout in IB, the only constraint options I have available to select for ContentView are the following:


leading = 0 to ScrollView

top = 0 to ScrollView

trailing = -325 to Scrollview

bottom = -333 to Scollview

width = 700

height = 700


  • How were you able to set constraints for your ContentView to be pinned to the SuperView?
  • Are you using Auto Layout to set constraints?
  • Is your ContentView located within the Scrollview?


PS: Thanks for all the help ! Looks like we are getting close

Hey Claude,


Please let me know how you got it working