iosswiftuikituiscrollview

ScrollView is not scrolling


I have the following scrollview but it's not scrolling. Can someone please help?

private func setupUI() {
    let scrollView = UIScrollView()
    scrollView.translatesAutoresizingMaskIntoConstraints = false
    scrollView.showsHorizontalScrollIndicator = false
    scrollView.showsVerticalScrollIndicator = false
    view.addSubview(scrollView)
    
    NSLayoutConstraint.activate([
        scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
        scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
    ])
    
    let contentView = UIView()
    contentView.translatesAutoresizingMaskIntoConstraints = false
    scrollView.addSubview(contentView)
    
    
    NSLayoutConstraint.activate([
        contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
        contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
        contentView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor),
        contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor)
    ])
    
    let closeButton = UIButton(type: .custom)
    closeButton.setBackgroundImage(UIImage(named: "close-x"), for: .normal)
    closeButton.addTarget(self, action: #selector(closeTapped), for: .touchUpInside)
    closeButton.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(closeButton)
    
    let imageView1 = UIImageView()
    imageView1.image = UIImage(named: "freetrialtop")
    imageView1.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(imageView1)
    
    let overlayButton = UIButton(type: .custom)
    overlayButton.setImage(UIImage(named: "freetrialbtn"), for: .normal)
    overlayButton.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(overlayButton)
    
    let titleLabel = UILabel()
    titleLabel.text = "Try Test PRO"
    titleLabel.font = UIFont(name: "WorkSans-SemiBold", size: 28)
    titleLabel.textColor = UIColor(red: 0.15, green: 0.16, blue: 0.20, alpha: 1.00)
    titleLabel.textAlignment = .center
    titleLabel.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(titleLabel)
    
    let subtitleLabel = UILabel()
    subtitleLabel.text = "7 days free, then $89.99/per year"
    subtitleLabel.font = UIFont(name: "WorkSans-Regular", size: 15)
    subtitleLabel.textAlignment = .center
    subtitleLabel.textColor = .gray
    subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(subtitleLabel)
    
    let trialLabel = UILabel()
    trialLabel.text = "How your trial works:"
    trialLabel.font = UIFont(name: "WorkSans-SemiBold", size: 14)
    trialLabel.textAlignment = .left
    trialLabel.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(trialLabel)
    
    let imageView2 = UIImageView()
    imageView2.image = UIImage(named: "freetrialline")
    imageView2.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(imageView2)
    
    let priceLabel = UILabel()
    priceLabel.text = "7 days free, then $89.99/per year"
    priceLabel.font = UIFont(name: "WorkSans-Regular", size: 14)
    priceLabel.textAlignment = .center
    priceLabel.textColor = .gray
    priceLabel.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(priceLabel)
    
    let weeklyPriceLabel = UILabel()
    weeklyPriceLabel.text = "Only $1.73 / week"
    weeklyPriceLabel.font = UIFont(name: "WorkSans-SemiBold", size: 24)
    weeklyPriceLabel.textAlignment = .center
    weeklyPriceLabel.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(weeklyPriceLabel)
    
    let subscribeButton = UIButton(type: .custom)
    subscribeButton.setImage(UIImage(named: "freetrialbtn1"), for: .normal)
    subscribeButton.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(subscribeButton)
    
    
    let cancelStackView = UIStackView()
    cancelStackView.axis = .horizontal
    cancelStackView.alignment = .center
    cancelStackView.spacing = 8
    cancelStackView.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(cancelStackView)
    
    let cancelLabel = UILabel()
    cancelLabel.text = "Cancel anytime. Secure with App Store"
    cancelLabel.font = UIFont(name: "Inter-Regular", size: 14)
    cancelLabel.textAlignment = .center
    cancelLabel.textColor = .darkGray
    cancelLabel.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(cancelLabel)
    
    let cancelImage = UIImageView(image: UIImage(systemName: "checkmark.shield"))
    cancelImage.tintColor = .gray
    cancelImage.setContentHuggingPriority(.defaultHigh, for: .horizontal)
    cancelImage.setContentCompressionResistancePriority(.required, for: .horizontal)
    
    
    cancelStackView.addArrangedSubview(cancelImage)
    cancelStackView.addArrangedSubview(cancelLabel)
    
    
    let todayLabel = UILabel()
    todayLabel.text = "Today - Free trial starts"
    todayLabel.font = UIFont(name: "WorkSans-SemiBold", size: 16)
    todayLabel.textAlignment = .left
    todayLabel.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(todayLabel)
    
    let todayDetailLabel = UILabel()
    todayDetailLabel.text = "Enjoy full access free for 7 days."
    todayDetailLabel.font = UIFont(name: "WorkSans-Regular", size: 12)
    todayDetailLabel.textAlignment = .left
    todayDetailLabel.textColor = .gray
    todayDetailLabel.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(todayDetailLabel)
    
    let reminderLabel = UILabel()
    reminderLabel.text = "\(getFutureDate(daysAhead: 5)) - Email reminder"
    reminderLabel.font = UIFont(name: "WorkSans-SemiBold", size: 16)
    reminderLabel.textAlignment = .left
    reminderLabel.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(reminderLabel)
    
    let reminderDetailLabel = UILabel()
    reminderDetailLabel.text = "We’ll let you know when your trial is ending."
    reminderDetailLabel.font = UIFont(name: "WorkSans-Regular", size: 12)
    reminderDetailLabel.textAlignment = .left
    reminderDetailLabel.textColor = .gray
    reminderDetailLabel.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(reminderDetailLabel)
    
    let memberLabel = UILabel()
    memberLabel.text = "\(getFutureDate(daysAhead: 7)) - Become a member"
    memberLabel.font = UIFont(name: "WorkSans-SemiBold", size: 16)
    memberLabel.textAlignment = .left
    memberLabel.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(memberLabel)
    
    let memberDetailLabel = UILabel()
    memberDetailLabel.text = "Your trial ends unless canceled. Enjoy!"
    memberDetailLabel.font = UIFont(name: "WorkSans-Regular", size: 12)
    memberDetailLabel.textAlignment = .left
    memberDetailLabel.textColor = .gray
    memberDetailLabel.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(memberDetailLabel)
    
    NSLayoutConstraint.activate([
        imageView1.topAnchor.constraint(equalTo: contentView.topAnchor),
        imageView1.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
        imageView1.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
        imageView1.heightAnchor.constraint(equalToConstant: 205),
        
        closeButton.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
        closeButton.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
        
        overlayButton.centerXAnchor.constraint(equalTo: imageView1.centerXAnchor),
        overlayButton.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
        overlayButton.widthAnchor.constraint(equalToConstant: 243),
        overlayButton.heightAnchor.constraint(equalToConstant: 66),
        
        titleLabel.topAnchor.constraint(equalTo: overlayButton.bottomAnchor, constant: 20),
        titleLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
        
        subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 5),
        subtitleLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
        
        trialLabel.topAnchor.constraint(equalTo: imageView1.bottomAnchor, constant: 20),
        trialLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
        
        imageView2.topAnchor.constraint(equalTo: trialLabel.bottomAnchor, constant: 25),
        imageView2.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
        imageView2.widthAnchor.constraint(equalToConstant: 32),
        imageView2.heightAnchor.constraint(equalToConstant: 212),
        
        todayLabel.topAnchor.constraint(equalTo: imageView2.topAnchor),
        todayLabel.leadingAnchor.constraint(equalTo: imageView2.trailingAnchor, constant: 10),
        
        todayDetailLabel.topAnchor.constraint(equalTo: todayLabel.bottomAnchor, constant: 5),
        todayDetailLabel.leadingAnchor.constraint(equalTo: imageView2.trailingAnchor, constant: 10),
        
        
        reminderLabel.topAnchor.constraint(equalTo: todayDetailLabel.bottomAnchor, constant: 40),
        reminderLabel.leadingAnchor.constraint(equalTo: imageView2.trailingAnchor, constant: 10),
        
        
        reminderDetailLabel.topAnchor.constraint(equalTo: reminderLabel.bottomAnchor, constant: 5),
        reminderDetailLabel.leadingAnchor.constraint(equalTo: imageView2.trailingAnchor, constant: 10),
        
        memberLabel.topAnchor.constraint(equalTo: reminderDetailLabel.bottomAnchor, constant: 40),
        memberLabel.leadingAnchor.constraint(equalTo: imageView2.trailingAnchor, constant: 10),
        
        memberDetailLabel.topAnchor.constraint(equalTo: memberLabel.bottomAnchor, constant: 5),
        memberDetailLabel.leadingAnchor.constraint(equalTo: imageView2.trailingAnchor, constant: 10),
        
        priceLabel.topAnchor.constraint(equalTo: imageView2.bottomAnchor, constant: 50),
        priceLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
        
        weeklyPriceLabel.topAnchor.constraint(equalTo: priceLabel.bottomAnchor, constant: 5),
        weeklyPriceLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
        
        subscribeButton.topAnchor.constraint(equalTo: weeklyPriceLabel.bottomAnchor, constant: 20),
        subscribeButton.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
        subscribeButton.widthAnchor.constraint(equalToConstant: 335),
        subscribeButton.heightAnchor.constraint(equalToConstant: 48),
        
        cancelStackView.topAnchor.constraint(equalTo: subscribeButton.bottomAnchor, constant: 10),
        cancelStackView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor)
    ])

Solution

  • If we take your code and make only one change:

    contentView.backgroundColor = .green
    

    and run it, we'd expect to see a big, green background... correct?

    Give it a try -- no green!

    You have added a bunch of views, buttons, labels, etc as subviews to contentView, but you have not defined a bottom constraint for that view.

    Add this line at the very end of your constraints block:

    cancelStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0.0)
    

    You should now see the green background.

    If you still do not get scrolling, it's because there is not enough height to need to scroll. For example, your code on an iPhone 15 Pro - all the UI elements fit without scrolling.

    If we change the top anchor of your cancelStackView from:

    cancelStackView.topAnchor.constraint(equalTo: subscribeButton.bottomAnchor, constant: 10),
    

    to:

    cancelStackView.topAnchor.constraint(equalTo: subscribeButton.bottomAnchor, constant: 150),
    

    it will "push" that element down far enough that we need to scroll ... and we will be able to scroll.