0

Here is the issue: I need to add a view containing other subviews to a scroll view programmatically. In addition, I also need to make the frame of such a view to stick to the bounds of the main super view. The code below shows the approach I was trying to implement, but as you can see from the pictures below the 'contentView' is not updating its frame size when the screen is rotated. The initial code is taken from here for demonstration purposes. Any help would be greatly appreciated.

import UIKit

class TestViewController : UIViewController {
    
    var contentViewSize = CGSize()
    
    let contentView: UIView = {
        let view = UIView()
        view.backgroundColor = .magenta
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    let labelOne: UILabel = {
        let label = UILabel()
        label.text = "Scroll Top"
        label.backgroundColor = .red
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    let labelTwo: UILabel = {
        let label = UILabel()
        label.text = "Scroll Bottom"
        label.backgroundColor = .green
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    let scrollView: UIScrollView = {
        let v = UIScrollView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .cyan
        return v
    }()
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        contentViewSize = view.bounds.size
        labelTwo.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: (contentViewSize.width - labelTwo.frame.size.width - 16.0)).isActive = true
        
        labelTwo.topAnchor.constraint(equalTo: contentView.topAnchor, constant: (contentViewSize.height - labelTwo.frame.size.height - 16.0)).isActive = true
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        contentViewSize = view.bounds.size
        
        view.backgroundColor = .yellow
        
        self.view.addSubview(scrollView)
        
        scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 8.0).isActive = true
        scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 8.0).isActive = true
        scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -8.0).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -8.0).isActive = true
        
        scrollView.addSubview(contentView)
        
        contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 16).isActive = true
        contentView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 16).isActive = true
        contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 16).isActive = true
        contentView.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 16).isActive = true
        
        contentView.addSubview(labelOne)
        
        labelOne.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16.0).isActive = true
        labelOne.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16.0).isActive = true
        
        contentView.addSubview(labelTwo)
        
        labelTwo.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: (contentViewSize.width - labelTwo.frame.size.width - 16.0)).isActive = true
        
        labelTwo.topAnchor.constraint(equalTo: contentView.topAnchor, constant: (contentViewSize.height - labelTwo.frame.size.height - 16.0)).isActive = true
        
        labelTwo.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -16.0).isActive = true
        labelTwo.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16.0).isActive = true
        
    }
    
}

portrait view

landscape view

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Andrey
  • 25
  • 4

1 Answers1

1

You made some odd changes to the code from the answer you linked to. Also, that answer is a little out-of-date.

Here's a better example. Assuming you want only vertical scrolling, this will:

  • add a Cyan scroll view, inset 8-pts on each side from the safe-area
  • add a Magenta "content view" to the scroll view, with 16-pts on each side constrained to the scroll view's contentLayoutGuide, with a width 32-pts less than the scroll view's frame (16-pts on each side)
  • add a label at top-left of the content view
  • add a label at bottom-right of the content view
  • constrain the bottom label 1500-pts below the top label (so it will scroll vertically)

Code:

class ScrollTestViewController : UIViewController {
    
    let contentView: UIView = {
        let view = UIView()
        view.backgroundColor = .magenta
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    let labelOne: UILabel = {
        let label = UILabel()
        label.text = "Scroll Top"
        label.backgroundColor = .red
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    let labelTwo: UILabel = {
        let label = UILabel()
        label.text = "Scroll Bottom"
        label.backgroundColor = .green
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    let scrollView: UIScrollView = {
        let v = UIScrollView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .cyan
        return v
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .yellow
        
        // add the scroll view
        self.view.addSubview(scrollView)
        
        // add contentView to scroll view
        scrollView.addSubview(contentView)
        
        // add two labels to contentView
        contentView.addSubview(labelOne)
        contentView.addSubview(labelTwo)
                
        // respect safe-area
        let g = view.safeAreaLayoutGuide
        
        // if you want to ignore the safe-area (bad idea),
        //  use this instead
        //let g = view!
        //scrollView.contentInsetAdjustmentBehavior = .never
        
        // we're going to constrain the contentView to the scroll view's content layout guide
        let scg = scrollView.contentLayoutGuide
        
        NSLayoutConstraint.activate([
            
            // constrain scrollView Top / Leading / Trailing / Bottom to view (safe-area)
            //  with 8-pts on each side
            scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 8.0),
            scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
            scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
            scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -8.0),
            
            // constrain contentView Top / Leading / Trailing / Bottom to scroll view's Content Layout Guide
            //  with 16-pts on each side
            contentView.topAnchor.constraint(equalTo: scg.topAnchor, constant: 16.0),
            contentView.leadingAnchor.constraint(equalTo: scg.leadingAnchor, constant: 16.0),
            contentView.trailingAnchor.constraint(equalTo: scg.trailingAnchor, constant: -16.0),
            contentView.bottomAnchor.constraint(equalTo: scg.bottomAnchor, constant: -16.0),

            // if we only want vertical scrolling, constrain contentView Width
            // to scrollView's Frame Layout Guide minus 32-pts (because we have 16-pts on each side)
            contentView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor, constant: -32.0),
            
            // constrain labelOne Top / Leading 16-pts to contentView Top / Leading
            //  (so it shows up at top-left corner)
            labelOne.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16.0),
            labelOne.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16.0),
            
            // constrain labelTwo Bottom / Trailing 16-pts to contentView Bottom / Trailing
            //  (so it shows up at bottom-right corner)
            labelTwo.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16.0),
            labelTwo.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16.0),
            
            // constrain labelTwo Top to labelOne Bottom + 1500-pts
            //  so we'll have some vertical scrolling to get to it
            labelTwo.topAnchor.constraint(equalTo: labelOne.bottomAnchor, constant: 1500.0),
            
        ])
                
    }
    
}
DonMag
  • 69,424
  • 5
  • 50
  • 86
  • @Andrey - if this fixed the issue, be sure to the "Accept" the answer (for the benefit of others who may come across your post). – DonMag Sep 18 '20 at 17:31