Using ScrollView Programmatically in Swift 3

Viewed 81213

I have searched other questions and seem to still have some trouble creating my scrollView programmatically with autolayout in swift 3. I am able to get my scrollview to show up as shown in the picture below, but when I scroll to the bottom my other label does not show up and the 'scroll top' label does not disappear.

ScrollView

Hoping someone can help review my code below!

import UIKit

class ViewController: UIViewController {

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
}()


override func viewDidLoad() {
    super.viewDidLoad()

    let screensize: CGRect = UIScreen.main.bounds
    let screenWidth = screensize.width
    let screenHeight = screensize.height
    var scrollView: UIScrollView!
    scrollView = UIScrollView(frame: CGRect(x: 0, y: 120, width: screenWidth, height: screenHeight))
    scrollView.contentSize = CGSize(width: screenWidth, height: 2000)
    scrollView.addSubview(labelOne)
    scrollView.addSubview(labelTwo)


    view.addSubview(labelOne)
    view.addSubview(labelTwo)
    view.addSubview(scrollView)

    // Visual Format Constraints
    view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": labelOne]))
    view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-100-[v0]", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": labelOne]))

    // Using iOS 9 Constraints in order to place the label past the iPhone 7 view
    view.addConstraint(NSLayoutConstraint(item: labelTwo, attribute: .top, relatedBy: .equal, toItem: labelOne, attribute: .bottom, multiplier: 1, constant: screenHeight + 200))
    view.addConstraint(NSLayoutConstraint(item: labelTwo, attribute: .right, relatedBy: .equal, toItem: labelOne, attribute: .right, multiplier: 1, constant: 0))
    view.addConstraint(NSLayoutConstraint(item: labelTwo, attribute: .left, relatedBy: .equal, toItem: labelOne, attribute: .left, multiplier: 1, constant: 0)     

    }

}
6 Answers

For me this work like charm

class WithDrawConfirmationViewController: UIViewController, WithDrawConfirmationViewProtocol {

    var presenter: WithDrawConfirmationPresenterProtocol?
    private let viewbackgroundColor = UIColor(hexString: "#F5F5F8")
    // MARK:- View properties
    lazy var scrollView: UIScrollView = {
        let scrollView = UIScrollView()
        //view.backgroundColor = .red
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        return scrollView
    }()
    
    lazy var contentView: UIView = {
        let view = UIView()
        //view.backgroundColor = .green
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    lazy var headerView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    private lazy var titleLabel:UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textColor = UIColor.themeBlack
        label.text = "Withdraw Confirmation"//"Loading.."
        label.textAlignment = .center
        label.font = UIFont(name: "AvenirNext-DemiBold", size: 20)
        label.numberOfLines = 0
        label.lineBreakMode = .byWordWrapping
        return label
    }()
    
    
    private lazy var descriptionLabel:UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textColor = UIColor.themeGray
        label.text = "Please confirm your fixed deposit details before withdrawing your deposit"// "Loading.."
        label.textAlignment = .center
        label.font = UIFont(name: "AvenirNext-Medium", size: 14)
        label.numberOfLines = 0
        label.lineBreakMode = .byWordWrapping
        label.setContentHuggingPriority(1000, for: .vertical)
        return label
    }()
    
    private lazy var instructionLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textColor = UIColor.themeGray
        label.text = "Complete fixed deposit will be withdrawn and the money will be debited to your account within 1-2 working days."//"Loading.."
        label.textAlignment = .left
        label.font = UIFont(name: "AvenirNext-Medium", size: 14)
        label.numberOfLines = 0
        label.lineBreakMode = .byWordWrapping
        return label
    }()
    
    private lazy var warningLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textColor = UIColor.themeBlue
        label.text = "Axis bank will levy a penalty of 1% - 2% for premature withdrawal"//"Loading.."
        label.textAlignment = .left
        label.font = UIFont(name: "AvenirNext-DemiBold", size: 13)
        label.numberOfLines = 0
        label.lineBreakMode = .byWordWrapping
        return label
    }()
    
    private lazy var warningView: UIView = {
        let view = UIView()
        view.cornerradius = 5
        view.backgroundColor = UIColor(hexFromString: "#E8F4FD")
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    private lazy var fdInformationDetailView: FDPurchaseDetailView = {
        let view = FDPurchaseDetailView(isPoweredByViewVisible: false)
        //view.backgroundColor = .red
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    private lazy var footerCTAView: FooterButtonViewView = {
        let view = FooterButtonViewView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    // MARK:- Life cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        contentView.backgroundColor = viewbackgroundColor
        scrollView.backgroundColor = viewbackgroundColor
        setUpUI()
        presenter?.viewDidLoadTriggered()
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
    }
    
    private func setUpUI() {
        view.addSubview(scrollView)
        scrollView.addSubview(contentView)
        view.backgroundColor = viewbackgroundColor
        addFooter()
        setUpHeaderView()
        setUpFDDetailView()
        setUpInformationAndWarningView()
        //scrollView.backgroundColor = .lightGray
        scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: footerCTAView.topAnchor).isActive = true
        scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 100, right: 0)
        
        contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
        contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
        contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
        contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
        contentView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
        //contentView.bottomAnchor.constraint(equalTo: footerCTAView.topAnchor).isActive = true
    }
    
    private func setUpHeaderView() {
        contentView.addSubview(headerView)
        headerView.addSubviews([titleLabel, descriptionLabel])
        
        headerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
        headerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
        headerView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 4).isActive = true
       
        titleLabel.leadingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: 8).isActive = true
        titleLabel.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
        titleLabel.topAnchor.constraint(equalTo: headerView.topAnchor, constant: 8).isActive = true
        
        descriptionLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant:  20).isActive = true
        descriptionLabel.leadingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: 8).isActive = true
        descriptionLabel.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
        descriptionLabel.bottomAnchor.constraint(equalTo: headerView.bottomAnchor, constant: -20).isActive = true
    }
    
    
    private func setUpFDDetailView() {
        contentView.addSubview(fdInformationDetailView)
        fdInformationDetailView.topAnchor.constraint(equalTo: headerView.bottomAnchor).isActive = true
        fdInformationDetailView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
        fdInformationDetailView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
       
    }
    
    private func setUpInformationAndWarningView() {
        contentView.addSubview(instructionLabel)
        
        instructionLabel.topAnchor.constraint(equalTo: fdInformationDetailView.bottomAnchor, constant: 20).isActive = true
        instructionLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
        instructionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
        
        warningView.addSubview(warningLabel)
        warningLabel.topAnchor.constraint(equalTo: warningView.topAnchor, constant: 8).isActive = true
        warningLabel.leadingAnchor.constraint(equalTo: warningView.leadingAnchor, constant: 20).isActive = true
        warningLabel.trailingAnchor.constraint(equalTo: warningView.trailingAnchor, constant: -20).isActive = true
        warningLabel.bottomAnchor.constraint(equalTo: warningView.bottomAnchor, constant: -8).isActive = true
        
        contentView.addSubview(warningView)
        
        warningView.topAnchor.constraint(equalTo: instructionLabel.bottomAnchor, constant: 8).isActive = true
        warningView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
        warningView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
       //warningView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        warningView.bottomAnchor.constraint(greaterThanOrEqualTo: contentView.bottomAnchor, constant: -20).isActive = true
        
    }
    
    private func addFooter() {
        view.addSubview(footerCTAView)
        footerCTAView.delegate = self
        footerCTAView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        footerCTAView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        footerCTAView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
    }
}

Copy and paste this controller in your project

class BaseScrollViewController: UIViewController {

lazy var contentViewSize = CGSize(width: self.view.frame.width, height: self.view.frame.height + 100)
lazy var scrollView: UIScrollView = {
    let view = UIScrollView(frame: .zero)
    view.backgroundColor = .white
    view.frame = self.view.bounds
    view.contentSize = contentViewSize
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()
lazy var containerView: UIView = {
    let v = UIView()
    v.backgroundColor = .white
    v.frame.size = contentViewSize
    return v
}()

override func viewDidLoad() {
    view.backgroundColor = .white
    view.addSubview(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

    scrollView.addSubview(containerView)
    setupContainer(containerView)
    super.viewDidLoad()
    
}

public func setupContainer(_ container: UIView) {
    
}

}

Usage for above code:

class ClientViewController: BaseScrollViewController {
override func viewDidLoad() {
    super.viewDidLoad()
    // do your stuff here
}

override func setupContainer(_ container: UIView) {
    // add views here
}

}

These answers do not work with large titles in the navigation bar. Make sure you have the code below in your viewDidLoad() method of your view controller:

self.navigationController?.navigationBar.prefersLargeTitles = false
Related