1

I have some problems with UIScrollView. Trying to add scrollable are for some sliders but nothing is working. I think it is because I'm using anchors for layout. Please, explain to me, what I'm doing wrong. Eventually, I need to have a scrollable area for iPhone SE version that user can use sliders.

iPhone X and iPhone SE

Here is my code, where I'm creating scrollview and adding all the sliders on it and putting anchors in relation with scrollview.

    //MARK: Scroll View
    scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 812))
    scrollView.backgroundColor = .gray
    scrollView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(scrollView)
    scrollView.topAnchor.constraint(equalTo: collectionView.bottomAnchor, constant: 0).isActive = true
    scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
    scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
    scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
    scrollView.contentSize = CGSize(width: view.bounds.width, height: 812)

    //MARK: Brightness Label
    brightnessLabel = UILabel()
    brightnessLabel.text = "Brightness"
    brightnessLabel.font = UIFont(name: "Avenir", size: 14)
    brightnessLabel.translatesAutoresizingMaskIntoConstraints = false
    scrollView.addSubview(brightnessLabel)
    brightnessLabel.topAnchor.constraint(equalTo: collectionView.bottomAnchor, constant: 10).isActive = true
    brightnessLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: view.bounds.width / -2).isActive = true
    brightnessLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
    brightnessLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true

    //MARK: Brightness Slider
    brightnessSlider = UISlider()
    brightnessSlider.setThumbImage(UIImage(named: "sliderThumb"), for: .normal)
    brightnessSlider.tintColor = .black
    brightnessSlider.minimumValue = 0
    brightnessSlider.maximumValue = 100
    brightnessSlider.translatesAutoresizingMaskIntoConstraints = false
    brightnessSlider.setValue(50, animated: true)
    scrollView.addSubview(brightnessSlider)
    brightnessSlider.topAnchor.constraint(equalTo: brightnessLabel.bottomAnchor, constant: 5).isActive = true
    brightnessSlider.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
    brightnessSlider.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
    brightnessSlider.heightAnchor.constraint(equalToConstant: 25).isActive = true

    //MArk: Brightness Value Label
    brightnessValueLabel = UILabel()
    brightnessValueLabel.text = "50"
    brightnessValueLabel.font = UIFont(name: "Avenir", size: 14)
    brightnessValueLabel.translatesAutoresizingMaskIntoConstraints = false
    scrollView.addSubview(brightnessValueLabel)
    brightnessValueLabel.topAnchor.constraint(equalTo: collectionView.bottomAnchor, constant: 10).isActive = true
    brightnessValueLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
    brightnessValueLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant:  view.bounds.width - 30).isActive = true
    brightnessValueLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true

    //MARK: Contrast Label
    contrastLabel = UILabel()
    contrastLabel.text = "Contrast"
    contrastLabel.font = UIFont(name: "Avenir", size: 14)
    contrastLabel.translatesAutoresizingMaskIntoConstraints = false
    scrollView.addSubview(contrastLabel)
    contrastLabel.topAnchor.constraint(equalTo: brightnessSlider.bottomAnchor, constant: 10).isActive = true
    contrastLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: view.bounds.width / -2).isActive = true
    contrastLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
    contrastLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true

    //MARK: Contrast Slider
    contrastSlider = UISlider()
    contrastSlider.setThumbImage(UIImage(named: "sliderThumb"), for: .normal)
    contrastSlider.tintColor = .black
    contrastSlider.minimumValue = 0
    contrastSlider.maximumValue = 100
    contrastSlider.translatesAutoresizingMaskIntoConstraints = false
    contrastSlider.setValue(50, animated: true)
    scrollView.addSubview(contrastSlider)
    contrastSlider.topAnchor.constraint(equalTo: contrastLabel.bottomAnchor, constant: 5).isActive = true
    contrastSlider.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
    contrastSlider.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
    contrastSlider.heightAnchor.constraint(equalToConstant: 25).isActive = true

    //MArk: Contrast Value Label
    contrastValueLabel = UILabel()
    contrastValueLabel.text = "50"
    contrastValueLabel.font = UIFont(name: "Avenir", size: 14)
    contrastValueLabel.translatesAutoresizingMaskIntoConstraints = false
    scrollView.addSubview(contrastValueLabel)
    contrastValueLabel.topAnchor.constraint(equalTo: brightnessSlider.bottomAnchor, constant: 10).isActive = true
    contrastValueLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
    contrastValueLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant:  view.bounds.width - 30).isActive = true
    contrastValueLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true

    //MARK: Saturation Label
    saturationLabel = UILabel()
    saturationLabel.text = "Saturation"
    saturationLabel.font = UIFont(name: "Avenir", size: 14)
    saturationLabel.translatesAutoresizingMaskIntoConstraints = false
    scrollView.addSubview(saturationLabel)
    saturationLabel.topAnchor.constraint(equalTo: contrastSlider.bottomAnchor, constant: 10).isActive = true
    saturationLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: view.bounds.width / -2).isActive = true
    saturationLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
    saturationLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true

    //MARK: Saturation Slider
    saturationSlider = UISlider()
    saturationSlider.setThumbImage(UIImage(named: "sliderThumb"), for: .normal)
    saturationSlider.tintColor = .black
    saturationSlider.minimumValue = 0
    saturationSlider.maximumValue = 100
    saturationSlider.translatesAutoresizingMaskIntoConstraints = false
    saturationSlider.setValue(50, animated: true)
    scrollView.addSubview(saturationSlider)
    saturationSlider.topAnchor.constraint(equalTo: saturationLabel.bottomAnchor, constant: 5).isActive = true
    saturationSlider.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
    saturationSlider.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
    saturationSlider.heightAnchor.constraint(equalToConstant: 25).isActive = true

    //MArk: Saturation Value Label
    saturationValueLabel = UILabel()
    saturationValueLabel.text = "50"
    saturationValueLabel.font = UIFont(name: "Avenir", size: 14)
    saturationValueLabel.translatesAutoresizingMaskIntoConstraints = false
    scrollView.addSubview(saturationValueLabel)
    saturationValueLabel.topAnchor.constraint(equalTo: contrastSlider.bottomAnchor, constant: 10).isActive = true
    saturationValueLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
    saturationValueLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant:  view.bounds.width - 30).isActive = true
    saturationValueLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true

    //MARK: Noise Label
    noiseLabel = UILabel()
    noiseLabel.text = "Noise"
    noiseLabel.font = UIFont(name: "Avenir", size: 14)
    noiseLabel.translatesAutoresizingMaskIntoConstraints = false
    scrollView.addSubview(noiseLabel)
    noiseLabel.topAnchor.constraint(equalTo: saturationSlider.bottomAnchor, constant: 10).isActive = true
    noiseLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: view.bounds.width / -2).isActive = true
    noiseLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
    noiseLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true

    //MARK: Noise Slider
    noiseSlider = UISlider()
    noiseSlider.setThumbImage(UIImage(named: "sliderThumb"), for: .normal)
    noiseSlider.tintColor = .black
    noiseSlider.minimumValue = 0
    noiseSlider.maximumValue = 100
    noiseSlider.translatesAutoresizingMaskIntoConstraints = false
    noiseSlider.setValue(50, animated: true)
    scrollView.addSubview(noiseSlider)
    noiseSlider.topAnchor.constraint(equalTo: noiseLabel.bottomAnchor, constant: 5).isActive = true
    noiseSlider.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
    noiseSlider.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
    noiseSlider.heightAnchor.constraint(equalToConstant: 25).isActive = true

    //MArk: Noise Value Label
    noiseValueLabel = UILabel()
    noiseValueLabel.text = "50"
    noiseValueLabel.font = UIFont(name: "Avenir", size: 14)
    noiseValueLabel.translatesAutoresizingMaskIntoConstraints = false
    scrollView.addSubview(noiseValueLabel)
    noiseValueLabel.topAnchor.constraint(equalTo: saturationSlider.bottomAnchor, constant: 10).isActive = true
    noiseValueLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
    noiseValueLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant:  view.bounds.width - 30).isActive = true
    noiseValueLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true

If somebody wonders, this is my view hierarchy, where all the items I need to be on scroll view are in fact on scroll view. I added gray color to scroll view to make it visable.

enter image description here

Rakesha Shastri
  • 11,053
  • 3
  • 37
  • 50
Tigran
  • 235
  • 5
  • 14
  • I highly suggest you read [this answer](https://stackoverflow.com/questions/51688114/my-swift-4-uiscrollview-with-autolayout-constraints-is-not-scrolling) and its comments. Also as an **important note: When you use Autolayout, then you shouldn't be setting the contentSize. The scrollView will size itself** – mfaani Aug 19 '18 at 01:58
  • I would also recommend sticking things below the image, within a Stack View Controller if you are not doing so already. That would probably help you out quite a bit too. They are amazingly helpful. – KSigWyatt Aug 19 '18 at 02:01
  • 1
    Also if things aren't working then I'd first start with a smaller number of subviews, then after I get things working it'd add more. Too many subview will confuse you – mfaani Aug 19 '18 at 02:04

1 Answers1

1

When you are using autolayout you do not need to specify contentSize, it will infer that from the subviews. The conditions are:

  • topmostView.topAnchor should be attached to scrollView.topAnchor
  • bottommostView.bottomAnchor should be attached to scrollView.bottomAnchor
  • Leading and trailing of scrollView should be attached to any view which has a width.

A few problems i see in your code.

  • You seem to have attached a label's leading and trailing to a scrollView which is not what you want to do most of the time because the scrollView width will be only as much as that of the label.

  • You also seem to have attached the scrollView leading and trailing at multiple places which will compute different leading and trailing constraints leading to conflicts. (I'm pretty sure if you check your log it will show some constraint breaking)

  • You have not attached the top or bottom of the topmost and bottommost views that are inside the scrollView to the scrollView's top and bottom.

Apple recommends that when using UIScrollView, have a dummy view (contentView) which will pinned to the top, leading, trailing, bottom of the scrollView and have a specified width. The height of that view will be calculated dynamically from the views you add to it. The same concept of the top of the topmost view attached to the contentView's top and bottom of the bottommost view attached to the contentView's bottom.

You can check my github repo where there is a minimalistic example which explains a UIScrollView with autolayout.

Rakesha Shastri
  • 11,053
  • 3
  • 37
  • 50
  • I checked everything and saw that I haven't attached anything to the bottom of the scroll view, just add one line and it started scrolling. Thank a lot. – Tigran Aug 19 '18 at 20:37
  • @Tigran yes but a lot of constraints would have broken if you check the logs. Please go through your constraints again and resolve the conflicts. It may scroll but it is not good practice to make the constraints break. – Rakesha Shastri Aug 19 '18 at 20:38
  • Where is that log? You mean the debug console? I haven't there any kind of logs... Everything is working just fine... – Tigran Aug 19 '18 at 20:49
  • @Tigran Oh then maybe the label might have stretched to fit the slider width or something. Good. If your debug console doesn't show conflicts then you are good to go! :D – Rakesha Shastri Aug 19 '18 at 20:50