2

so as you will see below I have a scrollview and I want to add it the the UIViewControllers root view. When I have it constrained to the top, right, bottom, and left I expect to see the red color take up the whole screen. This obviously works, but I want to add a subview to the scrollview that will wrap all the child views. How would I go about doing that?

I have added the view and I have set the same constraints except this time they are set from the wrapper view to the bounds of the UIScrollView, and the blue background color doesn't show anywhere. Also feel free to point out if this is a bad idea, but I thought I could just have it be constrained to the bottom and it will automatically extend the scrollviews content size as needed. This seems to work when I had all the subviews in the scrollview without a wrapper and the last view would extend the content size.

    scrollView = UIScrollView(frame: view.bounds)

    scrollView?.showsVerticalScrollIndicator = true
    scrollView?.backgroundColor = .red

    scrollView?.translatesAutoresizingMaskIntoConstraints = false

    view.addSubview(scrollView!)

    scrollView?.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
    scrollView?.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
    scrollView?.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
    scrollView?.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true

    //setup wrapper view
    let subviewWrapper = UIView()

    subviewWrapper.translatesAutoresizingMaskIntoConstraints = false

    scrollView?.addSubview(subviewWrapper)

    subviewWrapper.backgroundColor = .blue

    subviewWrapper.topAnchor.constraint(equalTo: (scrollView?.topAnchor)!).isActive = true
    subviewWrapper.leftAnchor.constraint(equalTo: (scrollView?.leftAnchor)!).isActive = true
    subviewWrapper.rightAnchor.constraint(equalTo: (scrollView?.rightAnchor)!).isActive = true
    subviewWrapper.bottomAnchor.constraint(equalTo: (scrollView?.bottomAnchor)!).isActive = true
user3832583
  • 371
  • 4
  • 19

1 Answers1

2

Actually this is a very good idea. I always set up my scrollViews this way. I usually call the view contentView, but it is the same idea.

You're almost there. You haven't yet given Auto Layout anything to go on to figure out the size of your subviewWrapper. The constraints you've set so far pin the subviewWrapper to the edges of the scrollView's content area, but this just establishes the fact that as the subviewWrapper grows, the content size of the scrollView will expand. Currently your subviewWrapper has 0 width and 0 height which is why you see no blue.

Below are 3 examples of how you might establish the size of your subviewWrapper.

Note: Each of the following examples is completely independent. Look at each one separately and as you try them, remember to delete the constraints added by the previous example.


Example 1: Make subviewWrapper 1000 x 1000:

Set constraints to make your subviewWrapper 1000 x 1000 and you will see the blue and it will scroll in both directions.

subviewWrapper.widthAnchor.constraint(equalToConstant: 1000).isActive = true
subviewWrapper.heightAnchor.constraint(equalToConstant: 1000).isActive = true

Example 2: Vertical only scrolling with content size 2X of scrollView height:

If you set the width of your subviewWrapper to be equal to the width of the scrollView then it will only scroll vertically. If you set the height of subviewWrapper to 2X the height of scrollView, then your blue area will be twice the height of the scrollView.

subviewWrapper.widthAnchor.constraint(equalTo: scrollView!.widthAnchor, multiplier: 1.0).isActive = true
subviewWrapper.heightAnchor.constraint(equalTo: scrollView!.heightAnchor, multiplier: 2.0).isActive = true

Example 3: Size of subviewWrapper set by its subviews:

You can also establish the size of your subviewWrapper by adding subviews to it that are fully specified in size and connected in a chain from the top of subviewWrapper to the bottom, and from side to side. If you do this, Auto Layout will have enough information to compute the size of your subviewWrapper

In this example, I've added a yellow 600 x 600 square to the subviewWrapper and set it 100 points from each edge. Without having explicitly set a size for subviewWrapper, Auto Layout can figure out that it is 800 x 800.

let yellowSquare = UIView()

yellowSquare.translatesAutoresizingMaskIntoConstraints = false
yellowSquare.backgroundColor = .yellow
subviewWrapper.addSubview(yellowSquare)

yellowSquare.widthAnchor.constraint(equalToConstant: 600).isActive = true
yellowSquare.heightAnchor.constraint(equalToConstant: 600).isActive = true

yellowSquare.topAnchor.constraint(equalTo: subviewWrapper.topAnchor, constant: 100).isActive = true
yellowSquare.leadingAnchor.constraint(equalTo: subviewWrapper.leadingAnchor, constant: 100).isActive = true
yellowSquare.trailingAnchor.constraint(equalTo: subviewWrapper.trailingAnchor, constant: -100).isActive = true
yellowSquare.bottomAnchor.constraint(equalTo: subviewWrapper.bottomAnchor, constant: -100).isActive = true
vacawama
  • 150,663
  • 30
  • 266
  • 294
  • 1
    This definitely seems to work, but what I cannot seem to get past is that my `rightAnchor` doesn't seem to work properly. I want the contentView(subviewWrapper) to constrain itself to the scrollView's `rightAnchor`. This doesn't give me the expected result I have to give it a widthAnchor to match the scrollView width. Is there a specific reason for that, or am I doing something wrong? – user3832583 Jun 08 '17 at 14:31
  • What are you trying to achieve by setting the subviewWrapper's right anchor? Are you trying to make it so that scrolling is limited to the vertical direction? – vacawama Jun 08 '17 at 14:35
  • yes that is what I am trying to do. I have it set to the width anchor, and that seems to work. I just don't understand why the other way doesn't work. Not really a big deal I just didn't like the look of it so I thought I would ask if there is a specific reason as to why this is done. – user3832583 Jun 08 '17 at 14:48
  • 1
    It's a difficult thing to understand, but setting constraints to the edges of the scrollView from subviews of the scrollView actually are relative to the content area controlled by the scrollView. So your right constraint of `0` just means that your `subviewWrapper` will go to the edge of the content area. But, since this is a scrollView, that content can scroll. To keep all of the content visible, you have to establish the width of the content area and that can be done by setting a width constraint or by connecting subviews in a chain. – vacawama Jun 08 '17 at 14:53