0

I have a scrollView created programmatically in MyView file.

let myScrollView: UIScrollView = {
    let scrollView = UIScrollView()
    scrollView.translatesAutoresizingMaskIntoConstraints = false
    scrollView.isUserInteractionEnabled = true
    return scrollView
}()

Using Visual Format Language I set all the constraints be equal to superView.

  func setupViews() {
    self.frame = UIScreen.main.bounds

    addSubview(myScrollView)

    addConstraintsWithFormat(format: "H:|[v0]|", views: myScrollView)
    addConstraintsWithFormat(format: "V:|[v0]|", views: myScrollView)    

}

My scrollView has a button and a label.

In viewController in viewDidLayoutSubviews I set contentSize:

myView.myScrollView.contentSize = CGSize(width: self.view.frame.size.width, height: self.view.frame.size.height + 200)

Everything works fine: I can scroll, the height of myScrollView is perfect, but the width perfect only for iPhone X.

Here is the example of iPhone SE:

enter image description here

ScrollView is wider than screen. In iPhone Plus it's smaller than screen.

What I'm doing wrong and how can I fix it?

Thank you in advance!

Jonagold
  • 81
  • 1
  • 9

2 Answers2

0

First, never do addSubview in layoutSubviews(). It means that you add your view at every layout. You may end up having several scrollViews in the hierarchy.

Second, you could just do a scrollView.frame = bounds and calculate your actual contentSize accordingly. The content size has nothing to do with the frame of the scrollView. You need to get the bottom most view and get his maxY. Same goes for X axis. If you want the content to only scroll vertically, something like contentSize = CGSize(width: width, height: lastViewInScrollView.frame.maxY) could work.

If you absolutely need VFL, at least calculate your contentSize properly (that is with the actual frames of the views contained within the scrollView)

As for your view which gets layouted outside the scrollView, it may be that you've set the wrong size for these views.

Francis.Beauchamp
  • 1,323
  • 15
  • 28
  • 1
    Francis, thank you for your suggestions. I did changed you mentioned but still have the same issue. – Jonagold Dec 18 '17 at 15:46
0

There is no need to calculate any explicit sizes when using auto layout and scroll view together. Create the scroll view in loadView (purely-programmatic apps will most likely do it here) or viewDidLoad:

override func loadView() {

    ...
    addScrollView()
    addScrollViewHeader()
    // add subviews
    addScrollViewFooter()

}

func addScrollView() {

    scrollView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(scrollView)
    scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
    scrollView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 1).isActive = true
    scrollView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 1).isActive = true

}

There are a few rules in Apple's documentation to make it work with autolayout such as, if I remember correctly, not depending on the scroll view itself to determine the size of any subviews within it. But the idea is to make sure that the outer-most views (top-most, bottom-most, left-most, right-most) are anchored to the scroll view and the scroll view's content size will automatically scale.

So what I do is create transparent views at the top and bottom that stretch the scroll view's content size for me and then whatever content goes in the middle will expand and contract the scroll view automatically. The reason I use these header/footer views instead of simply adding a constant to the top-most constraint and the bottom-most constraint (to create some extra padding at the top and bottom of the scroll view) is because constants appear to be ignored. There must ostensibly be "physical" contact between the view and the edges of the content size to stretch it.

func addScrollViewHeader() {

    scrollViewHeader.isUserInteractionEnabled = false
    scrollViewHeader.translatesAutoresizingMaskIntoConstraints = false
    scrollView.addSubview(scrollViewHeader)
    scrollViewHeader.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
    scrollViewHeader.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
    scrollViewHeader.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
    scrollViewHeader.heightAnchor.constraint(equalToConstant: Shared.statusBarHeight + 32).isActive = true

}

// subviews in the middle

func addScrollViewFooter() {

    scrollViewFooter.isUserInteractionEnabled = false
    scrollViewFooter.translatesAutoresizingMaskIntoConstraints = false
    scrollView.addSubview(scrollViewFooter)
    scrollViewFooter.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
    scrollViewFooter.topAnchor.constraint(equalTo: lastView.bottomAnchor).isActive = true
    scrollViewFooter.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
    scrollViewFooter.heightAnchor.constraint(equalToConstant: 32).isActive = true

    // and the most important constraint to make it work
    scrollViewFooter.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true

}
trndjc
  • 11,654
  • 3
  • 38
  • 51