UIScrollView Scrollable Content Size Ambiguity

2019-01-01 05:55发布

问题:

Fellow devs, I am having trouble with AutoLayout in Interface Builder (Xcode 5 / iOS 7). It\'s very basic and important so I think everyone should know how this properly works. If this is a bug in Xcode, it is a critical one!

So, whenever I have a view hierarchy such as this I run into trouble:

>UIViewController
>> UIView
>>>UIScrollView
>>>>UILabel (or any other comparable UIKit Element)

The UIScrollView has solid constraints, e.g., 50 px from every side (no problem). Then I add a Top Space constraint to the UILabel (no problem) (and I can even pin height / width of the label, changes nothing, but should be unneccessary due to the Label\'s intrinsic size)

The trouble starts when I add a trailing constraint to the UILabel:

E.g., Trailing Space to: Superview Equals: 25

Now two warnings occur - and I don\'t understand why:

A) Scrollable Content Size Ambiguity (Scroll View has ambiguous scrollable content height/width)

B) Misplaced Views (Label Expected: x= -67 Actual: x= 207

I did this minimal example in a freshly new project which you can download and I attached a screenshot. As you can see, Interface Builder expects the Label to sit outside of the UIScrollView\'s boundary (the orange dashed rectangle). Updating the Label\'s frame with the Resolve Issues Tool moves it right there.

Please note: If you replace the UIScrollView with a UIView, the behaviour is as expected (the Label\'s frame is correct and according to the constraint). So there seems to either be an issue with UIScrollView or I am missing out on something important.

When I run the App without updating the Label\'s frame as suggested by IB it is positioned just fine, exactly where it\'s supposed to be and the UIScrollView is scrollable. If I DO update the frame the Label is out of sight and the UIScrollView does not scroll.

Help me Obi-Wan Kenobi! Why the ambiguous layout? Why the misplaced view?

You can download the sample project here and try if you can figure out what\'s going on: https://github.com/Wirsing84/AutoLayoutProblem

\"Illustration

回答1:

NOTE: On iOS > 10.0, this hasn\'t been tested; therefore the solution could be incomplete in certain situations.

So I just sorted out in this way:

  1. Inside the UIScrollView add a UIView (we can call that contentView);

  2. In this contentView, set top, bottom, left and right margins to 0 (of course from the scrollView which is the superView); Set also align center horizontally and vertically;

Finished.

Now you can add all your views in that contentView, and the contentSize of the scrollView will be automatically resized according with the contentView.

Update:

Some special case is covered by this video posted by @Sergio in the comments below.



回答2:

This error took me a while to track down, initially I tried pinning the ScrollView\'s size but the error message clearly says \"content size\". I made sure everything I pinned from the top of the ScrollView was also pinned to the bottom. That way the ScrollView can calculate its content height by finding the height of all the objects & constraints. This solved the ambiguous content height, the width is pretty similar... Initially I had most things X centered in the ScrollView, but I had to also pin the objects to the side of the ScrollView. I don\'t like this because the iPhone6 might have a wider screen but it will remove the \'ambiguous content width\' error.



回答3:

It\'s answer for my self-answered question UIScrollView + Centered View + Ambigous Scrollable Content Size + many iPhone sizes.

But it fully cover your case too!

So, here is the initial state for simplest case:

  1. scrollView with 0 constraints to all edges
  2. Button centered Horizontal and Vertical with fixed Width and Height constraints
  3. And, of course - annoying warnings Has ambiguous scrollable content width and Has ambiguous scrollable content height.

\"1\"

All, that we have to do is:

  • Add 2 additional constraints, for example \"0\" for trailing and/or bottom space for our view (look at the example on screenshot below).

Important: you have to add trailing and/or bottom constraints. Not \"leading and top\" - it\'s not works!

\"2\"

You can check it in my example project, that demonstrating how to fix this issue.

P.S.

According logic - this action should cause \"conflicting constraints\". But no!

I don\'t know why it works and how Xcode detects which constraint is more prioritised (because I\'m not set priority for these constraints explicity). I\'ll be thankful if someone explain, why it works in comments below.



回答4:

I had the same problem and after searching through documentation and not finding an acceptable solution. You need to create an UIView as a subview of the UIScrollView as described below:

  • UIViewController
  • UIView \'Main View\'
    • UIScrollView
      • UIView \'Container View\'
        • [Your content]

The second step is to make the UIScrollView constraints. I did this with the top, bottom, leading and trailing constraints to its superView.

Next I added the constraints for the container of UIScrollView and here is where the problem begins. When you put the same constraints (top, bottom, leading and trailing) the Storyboard gives a warning message:

\"Has ambiguous scrollable content width\" and \"Has ambiguous scrollable content height\"

Continue with the same constraints above, and just add the constraints \"Equal Height\" and \"Equal Width\" to your Container View in relation to the Main View and not to your UIScrollView. In other words the Container View\'s constraints are tied to the UIScrollView\'s superview.

After that you will not have warnings in your Storyboard and you can continue adding the constraints for your subviews.

I hope this helps someone.



回答5:

I finally figured this out, I hope this is easier to understand.

You can often bypass that warning by adding a base UIView to the scrollview as a \"content view\", as they mentioned and make it the same size as your scroll view and, on this content view, set these 6 parameters.

\"enter

As you can see, you need 6 total parameters! Which.. under normal circumstances, you\'re duplicating 2 constraints but this is the way to avoid this storyboard error.



回答6:

I know I may be late, but next solution solves this kind of problems without additional code, just using storyboards:

For your content view you need to set constraints for leading/trailing/top/bottom spaces to scrollview and this will not change content view frame, like this: \"enter

Of course you need to create additional constraints for content view so scroll view could know content size. For example set fixed height and center x.

Hope this helps.



回答7:

iOS 12, Swift 4

The simplest way using autolayout:

  1. Add UIScrollView and pin it 0,0,0,0 to superview (or your desired size)
  2. Add UIView in ScrollView, pin it 0,0,0,0 to all 4 sides and center it horizontally and vertically.
  3. In size inspector, change \'bottom\' and \'align center Y\' priority to 250. (for horizontal scroll change \'trailing\' and \'align center X\')
  4. Add all views that you need into this view. Don\'t forget to set the bottom constraint on the lowest view.


回答8:

Check out this really quick and to the point tutorial on how to get the scroll view working and fully scrollable with auto layout. Now, the only thing that is still leaving me puzzled is why the scroll view content size is always larger then necessary..

http://samwize.com/2014/03/14/how-to-use-uiscrollview-with-autolayout/



回答9:

See below: content view in scrollview vertical and horizontal centralized. you got ambiguity error, always make sure two added into scrollview from your content view is 1).button space to container.2)trailing space to constraint that is highlighted in screenshot,

these constraint means in scroll is how much you can scroll after your content view height or width.

\"enter

it may help you.



回答10:

@Matteo Gobbi\'s answer is perfect, but in my case, the scrollview can\'t scroll, i remove \"align center Y\" and add \"height >=1\", the scrollview will became scrollable



回答11:

For me adding a contentView didn\'t really work as suggested. Moreover, it creates an overhead due to the added view (although I don\'t consider this a big problem). What worked best for me was just to turn off the ambiguity-checking for my scrollView. Everything is laying out nicely so I think it\'s okay in simple cases like mine. But keep in mind, that if other constraints for your scrollView break, the Interface-Builder will not warn you any more about it.

\"enter



回答12:

People who are struggling with uiscrollview not scrolling just set your content view\'s bottom constraint with your last view\'s bottom layout (which is inside of your content view). Do not forget to remove center Y constraint.

Rest all the constraints are same as defined above. Scrollview only concerned about getting maximum height from its content view, and we are setting it as last view\'s bottom constraint, which means scrollview automatically will change its content offset.

In my case last view was UILable with no of lines property = 0(which automatically adjust its height depending upon its content) so it dynamically increase uilable\'s height and eventually our scrollable area increases because of uilable\'s bottom layout is aligned with our content view\'s bottom, which forces scrollview to increase it\'s content offset.



回答13:

Using contentView (UView) as container inside UIScrollView, stick to edges (top, bottom, trailing, leading) of superview (UIScrollView) and contentView should have equal width and height to superview. Those are constraints. And call of method:

-(void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    self.scrollView.contentSize = self.contentView.frame.size;
}

Solved issues for me.



回答14:

I made a video on youTube
Scroll StackViews using only Storyboard in Xcode

I think 2 kind of scenarios can appear here.

The view inside the scrollView -

  1. does not have any intrinsic content Size (e.g UIView)
  2. does have its own intrinsic content Size (e.g UIStackView)

For a vertically scrollable view in both cases you need to add these constraints:

  1. 4 constraints from top, left, bottom and right.

  2. Equal width to scrollview (to stop scrolling horizontally)

You don\'t need any other constraints for views which have his own intrinsic content height.

\"enter


For views which do not have any intrinsic content height, you need to add a height constraint. The view will scroll only if the height constraint is more than the height of the scrollView.

\"enter



回答15:

I solved this kind of problem for my view by using \"Resolve auto layout issues\" > \"Add missing constraints\" for Selected View \"enter

The following two constraints solve my problem:

trailing = Stack View.trailing - 10
bottom = Stack View.bottom + 76

in which: trailing, bottom are the trailing, bottom of UIScrollView



回答16:

What I did is create a separate content view as shown here.

The content view is freeform and can has all subviews added to it with their own constraints in relation to the contentView.

The UIScrollView is added to the main view controller.

Programatically I add the contentView which is linked via IBOutlet to class and set the contentView of UIScrollView.

\"UIScrollView



回答17:

I was getting the same error.. i have done following

  1. View(Superview)
  2. ScrollView 0,0,600,600
  3. UIView inside ScrollView : 0,0,600,600
  4. UIView contains image view , label

Now add leading/trailing/top/bottom for scrollView(2) then UIView(3).

Select View(1) and View(3) set equally height and weight.. its solved my issue.

I have done the video that will help :

https://www.youtube.com/watch?v=s-CPN3xZS1A



回答18:

[tested in XCode 7.2 and for iOS 9.2]

What suppressed the Storyboard errors and warnings for me was setting the intrinsic size of the scroll view and the content view (in my case, a stackview) to Placeholder. This setting is found in the Size inspector in Storyboard. And it says - Setting a design time intrinsic content size only affects a view while editing in Interface Builder. The view will not have this intrinsic content size at runtime.

So, I guess we aren\'t going wrong by setting this.

Note: In storyboard, I have pinned all the edges of scrollview to the superview and all the edges of stackview to the scrollview. In my code, I have set the translatesAutoresizingMaskIntoConstraints as false for both the scrollview and the stackview. And I have not mentioned a content size. When the stackview grows dynamically, the constraints set in storyboard ensure that the stack is scrollable.

The storyboard warning was driving me mad and I didn\'t want to centre things horizontally or vertically just to suppress the warning.



回答19:

If anyone is getting a behavior where you notice the scroll bar on the right scrolls but the content doesn\'t move, this fact probably worth considering:

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.

That\'s from Apple\'s documentation. For example , if you accidentally pinned your top Label/Button/Img/View to the view outside the scroll area (Maybe a header or something just above the scrollView?) instead of the contentView, you\'d freeze your whole contentView in place.



回答20:

As mentioned in previous answers, you should add a custom view inside the scroll view:

\"The

Add all your subviews to the content view. At some point you will see a scroll content view has ambiguous content size warning, you should select the content view and click the \"Resolve auto layout issues\" button (at the bottom right corner of the IB layout), and select the \"Add missing constraints\" option.

\"enter

From now on when you run the project, the scroll view will automatically update it\'s content size, no additional code needed.



回答21:

You just have to make sure that there is a way to make a continuous line of constraints from the top to the bottom of the scrollview. [-X-X-X]

In my case - a horizontally scrolling scroll view - I also needed to add width and height constraints for each child of the scroll view, although Apple\'s technical note doesn\'t tell you this.



回答22:

Set the ViewController\'s (the one holding the UIScrollView) size to Freeform in the size inspector in Interface Builder and all should work.

Freeform setting in Size inspector in Interface Builder for the containing UIViewcontroller



回答23:

Swift 4+ approach:

1) Set UIScrollView top, bottom, left and right margins to 0

2) Inside the UIScrollView add a UIView and set top, bottom, leading, trailing margins to 0 ( equal to UIScrollView margins ).

3) The most important part is to set width and height, where height constraint should have a low priority.

private func setupConstraints() {
    // Constraints for scrollView
    scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
    scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
    // Constraints for containerView
    containerView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
    containerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
    containerView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
    containerView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
    containerView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
    let heightConstraint = containerView.heightAnchor.constraint(equalTo: scrollView.heightAnchor)
    heightConstraint.priority = UILayoutPriority(rawValue: 250)
    heightConstraint.isActive = true
}


回答24:

all the subviews inside a scrollview must have constraints touching all the edges of the scroll view, as its explained in the documentation, the height and width of a scroll view is calculated automatically by the measures in of the subviews, this means you need to have Trailing and leading constraints for width and Top and Bottom constraints for height.



回答25:

I think it is a 10 second work from now. I observed it in XCode 7.3 and made a video on it. Check here:

https://www.youtube.com/watch?v=yETZKqdaPiI

All you have to do, add a subview to UIScrollView with same width and height. Then select ViewController and press Reset to suggested constraint. Please check video for clear understanding.

Thanks