Swift 4-以编程方式创建垂直UIScroll

时间:2019-07-03 09:31:49

标签: ios swift uiscrollview

我有以下通过编程构建的Booking类(UIView),但无法使其滚动。我要求它只能垂直滚动。我不使用UIStackView,因为我实际使用的视图比下面的代码稍微复杂一些。下面提供的代码是简化版本。

我阅读了各种SO帖子,其中一些人说您需要在滚动视图内部包含一个包含所有子视图的视图(我目前正在这样做),其他人则说不需要。有人说您需要手动定义滚动视图的内容大小,而另一些人则说您不必这样做!这些观点中的每一个都正确吗?还是没关系?

当我将UIScrollView更新为所有视图高度的总和时,我看到垂直滚动指示器会上下移动,但实际内容不会移动!

如果已经设置了约束,那么scrollview应该能够计算高度吗?

我还遇到了视图在垂直方向上拉伸的问题,因此在设置约束时添加了具有优先权的内容。

这是代码:-

import UIKit

@IBDesignable
class Booking: UIView, UIScrollViewDelegate {

    //Layout Items
    let scrollView: UIScrollView = {
        let view = UIScrollView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.showsVerticalScrollIndicator = true
        view.showsHorizontalScrollIndicator = false
        view.isScrollEnabled = true
        return view
    }()

    let contentView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    //Header view and its children
    let headerContentView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1)
        return view
    }()

    let headerBookingLbl: UILabel = {
        let view = UILabel()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.textColor = UIColor.white
        view.numberOfLines = 3
        view.textAlignment = .center
        view.text = "Title Title Title"
        return view
    }()

    let aboutStack: UIStackView = {
        let view = UIStackView()
        view.isUserInteractionEnabled = true
        view.translatesAutoresizingMaskIntoConstraints = false
        view.axis = .horizontal
        view.alignment = .fill
        view.distribution = .fill
        view.spacing = 5
        view.backgroundColor = UIColor.clear
        return view
    }()

    let aboutIcon: UIImageView = {
        let view = UIImageView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.contentMode = .scaleAspectFit
        view.clipsToBounds = true
        view.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 0)
        return view
    }()

    let aboutLbl: UILabel = {
        let view = UILabel()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.textColor = UIColor.white
        view.text = "About Your Booking"
        view.numberOfLines = 1
        view.textAlignment = .natural
        return view
    }()


    //body view
    let haveABookingLbl: UILabel = {
        let view = UILabel()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.textColor = #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1)
        view.text = "Already have a booking?"
        view.numberOfLines = 0
        view.textAlignment = .center
        return view
    }()

    let enterABookingInfoLbl: UILabel = {
        let view = UILabel()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.textColor = #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1)
        view.text = "Enter your ticket reference number to gain access to bonus content"
        view.numberOfLines = 0
        view.textAlignment = .center
        return view
    }()

    let bookingReference: UITextField = {
        let view = UITextField()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.autocapitalizationType = .none
        view.borderStyle = UITextField.BorderStyle.roundedRect
        view.placeholder = "Ticket reference*"
        return view
    }()

    let lastName: UITextField = {
        let view = UITextField()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.autocapitalizationType = .none
        view.borderStyle = UITextField.BorderStyle.roundedRect
        view.placeholder = "Last name*"
        return view
    }()

    let submitBtn: UIButton = {
        let view = UIButton()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.setTitle("Submit", for: .normal)
        view.backgroundColor = #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1)
        return view
    }()

    let viewPrivacyPolicayLbl: UILabel = {
        let view = UILabel()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.isUserInteractionEnabled = true
        view.textColor = UIColor.black
        view.numberOfLines = 1
        view.textAlignment = .center

        //underline the text - use attributed string
        let text = "view privacy policy"
        let underlinedText = NSMutableAttributedString(string: text)
    underlinedText.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: NSRange(location: 0, length: text.count))
        view.attributedText = underlinedText

        return view
    }()


    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
    }

    //This override should prevent issue where you can drag scrollview 1px left/right which shows a white area behind the poster.
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        scrollView.contentOffset.x = 0
    }

    private func setup() {
        print("setup()")
        setupViews()
        setupConstraints()
        //scrollView.contentSize = contentView.bounds.size    ?? is this needed? doesnt seem to work!
        scrollView.delegate = self
    }

    private func setupViews() {
        print("setupViews()")

        //Add root scroll view
        addSubview(scrollView)
        scrollView.addSubview(contentView)

        //header contents
        contentView.addSubview(headerContentView)
        headerContentView.addSubview(headerBookingLbl)

        headerContentView.addSubview(aboutStack)
        aboutStack.addArrangedSubview(aboutIcon)
        aboutStack.addArrangedSubview(aboutLbl)


        //body contents
        contentView.addSubview(haveABookingLbl)
        contentView.addSubview(enterABookingInfoLbl)
        contentView.addSubview(bookingReference)
        contentView.addSubview(lastName)
        contentView.addSubview(submitBtn)
        contentView.addSubview(viewPrivacyPolicayLbl)

    } //setupViews

    private func setupConstraints() {
        print("setupConstraints()")

        let scrollViewConstraints = [
            scrollView.topAnchor.constraint(equalTo: topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: bottomAnchor),
            scrollView.widthAnchor.constraint(equalTo: widthAnchor),
            scrollView.centerXAnchor.constraint(equalTo: centerXAnchor),

            contentView.centerXAnchor.constraint(equalTo: centerXAnchor),
            contentView.widthAnchor.constraint(equalTo: widthAnchor),
            contentView.topAnchor.constraint(equalTo: topAnchor),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor),
        ]
        NSLayoutConstraint.activate(scrollViewConstraints)


        //header
        let headerViewConstraints = [
            //container
            headerContentView.topAnchor.constraint(equalTo: topAnchor),
            headerContentView.leadingAnchor.constraint(equalTo: leadingAnchor),
            headerContentView.trailingAnchor.constraint(equalTo: trailingAnchor),

            //headerBookingLbl
            headerBookingLbl.topAnchor.constraint(equalTo: headerContentView.topAnchor, constant: 20),
            headerBookingLbl.leadingAnchor.constraint(equalTo: headerContentView.leadingAnchor, constant: 20),
            headerBookingLbl.trailingAnchor.constraint(equalTo: headerContentView.trailingAnchor, constant: -20),

            //aboutStack
            aboutStack.topAnchor.constraint(equalTo: headerBookingLbl.bottomAnchor, constant: 20),
            aboutStack.centerXAnchor.constraint(equalTo: headerContentView.centerXAnchor),
            aboutStack.bottomAnchor.constraint(equalTo: headerContentView.bottomAnchor, constant: -20)
        ]
        NSLayoutConstraint.activate(headerViewConstraints)

        headerBookingLbl.setContentHuggingPriority(UILayoutPriority(rawValue: 1000), for: .vertical)


        //body
        let bodyViewConstraints = [
            //haveABookingLbl
            haveABookingLbl.topAnchor.constraint(equalTo: headerContentView.bottomAnchor, constant: 30),
            haveABookingLbl.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 60),
            haveABookingLbl.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -60),

            //enterABookingInfoLbl
            enterABookingInfoLbl.centerXAnchor.constraint(equalTo: centerXAnchor),
            enterABookingInfoLbl.topAnchor.constraint(equalTo: haveABookingLbl.bottomAnchor, constant: 15),
            enterABookingInfoLbl.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 60),
            enterABookingInfoLbl.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -60),

            //bookingReference
            bookingReference.heightAnchor.constraint(equalToConstant: 40),
            bookingReference.centerXAnchor.constraint(equalTo: centerXAnchor),
            bookingReference.topAnchor.constraint(equalTo: enterABookingInfoLbl.bottomAnchor, constant: 15),
            bookingReference.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 60),
            bookingReference.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -60),

            //lastName
            lastName.heightAnchor.constraint(equalToConstant: 40),
            lastName.centerXAnchor.constraint(equalTo: centerXAnchor),
            lastName.topAnchor.constraint(equalTo: bookingReference.bottomAnchor, constant: 15),
            lastName.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 60),
            lastName.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -60),

            //submitBtn
            submitBtn.centerXAnchor.constraint(equalTo: centerXAnchor),
            submitBtn.topAnchor.constraint(equalTo: lastName.bottomAnchor, constant: 15),
            submitBtn.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 60),
            submitBtn.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -60),

            viewPrivacyPolicayLbl.centerXAnchor.constraint(equalTo: centerXAnchor),
            viewPrivacyPolicayLbl.topAnchor.constraint(equalTo: submitBtn.bottomAnchor, constant: 15),
            viewPrivacyPolicayLbl.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 60),
            viewPrivacyPolicayLbl.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -60),
            //viewPrivacyPolicayLbl.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20)
        ]
        NSLayoutConstraint.activate(bodyViewConstraints)


        //Set hugging priority to prevent said items from being made larger than their intrinsic size
        haveABookingLbl.setContentHuggingPriority(UILayoutPriority(rawValue: 1000), for: .vertical)
        enterABookingInfoLbl.setContentHuggingPriority(UILayoutPriority(rawValue: 1000), for: .vertical)
        submitBtn.setContentHuggingPriority(UILayoutPriority(rawValue: 1000), for: .vertical)
        viewPrivacyPolicayLbl.setContentHuggingPriority(UILayoutPriority(rawValue: 1000), for: .vertical)

    } //setupConstraints
}

2 个答案:

答案 0 :(得分:1)

在滚动视图内添加内容视图的主要原因是使自动布局正常工作。内容视图必须填充滚动视图,添加到内容视图的每个子视图都需要使用其容器作为参考以正确的方式设置约束:

private func setupConstraints() {
        print("setupConstraints()")

        let scrollViewConstraints = [
            scrollView.topAnchor.constraint(equalTo: topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: bottomAnchor),
            scrollView.widthAnchor.constraint(equalTo: widthAnchor),
            scrollView.centerXAnchor.constraint(equalTo: centerXAnchor),

            contentView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
            contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
            contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor)
        ]
        NSLayoutConstraint.activate(scrollViewConstraints)

        // header
        let headerViewConstraints = [
            // container
            headerContentView.topAnchor.constraint(equalTo: contentView.topAnchor),
            headerContentView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            headerContentView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),

            // headerBookingLbl
            headerBookingLbl.topAnchor.constraint(equalTo: headerContentView.topAnchor, constant: 20),
            headerBookingLbl.leadingAnchor.constraint(equalTo: headerContentView.leadingAnchor, constant: 20),
            headerBookingLbl.trailingAnchor.constraint(equalTo: headerContentView.trailingAnchor, constant: -20),

            // aboutStack
            aboutStack.topAnchor.constraint(equalTo: headerBookingLbl.bottomAnchor, constant: 20),
            aboutStack.centerXAnchor.constraint(equalTo: headerContentView.centerXAnchor),
            aboutStack.bottomAnchor.constraint(equalTo: headerContentView.bottomAnchor, constant: -20)
        ]
        NSLayoutConstraint.activate(headerViewConstraints)

        headerBookingLbl.setContentHuggingPriority(UILayoutPriority(rawValue: 1000), for: .vertical)

        // body
        let bodyViewConstraints = [
            // haveABookingLbl
            haveABookingLbl.topAnchor.constraint(equalTo: headerContentView.bottomAnchor, constant: 30),
            haveABookingLbl.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 60),
            haveABookingLbl.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -60),

            // enterABookingInfoLbl
            enterABookingInfoLbl.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            enterABookingInfoLbl.topAnchor.constraint(equalTo: haveABookingLbl.bottomAnchor, constant: 15),
            enterABookingInfoLbl.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 60),
            enterABookingInfoLbl.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -60),

            // bookingReference
            bookingReference.heightAnchor.constraint(equalToConstant: 40),
            bookingReference.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            bookingReference.topAnchor.constraint(equalTo: enterABookingInfoLbl.bottomAnchor, constant: 15),
            bookingReference.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 60),
            bookingReference.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -60),

            // lastName
            lastName.heightAnchor.constraint(equalToConstant: 40),
            lastName.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            lastName.topAnchor.constraint(equalTo: bookingReference.bottomAnchor, constant: 15),
            lastName.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 60),
            lastName.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -60),

            // submitBtn
            submitBtn.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            submitBtn.topAnchor.constraint(equalTo: lastName.bottomAnchor, constant: 15),
            submitBtn.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 60),
            submitBtn.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -60),

            viewPrivacyPolicayLbl.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            viewPrivacyPolicayLbl.topAnchor.constraint(equalTo: submitBtn.bottomAnchor, constant: 15),
            viewPrivacyPolicayLbl.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 60),
            viewPrivacyPolicayLbl.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -60),
            viewPrivacyPolicayLbl.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20)
        ]
        NSLayoutConstraint.activate(bodyViewConstraints)


        // Set hugging priority to prevent said items from being made larger than their intrinsic size
        haveABookingLbl.setContentHuggingPriority(UILayoutPriority(rawValue: 1000), for: .vertical)
        enterABookingInfoLbl.setContentHuggingPriority(UILayoutPriority(rawValue: 1000), for: .vertical)
        submitBtn.setContentHuggingPriority(UILayoutPriority(rawValue: 1000), for: .vertical)
        viewPrivacyPolicayLbl.setContentHuggingPriority(UILayoutPriority(rawValue: 1000), for: .vertical)

} // setupConstraints

答案 1 :(得分:0)

根据内容大小设置滚动视图的高度,并将滚动视图的宽度设置为当前视图的宽度:-

let contentHeight = 700(depends on your requirement)
scrollView.contentSize = CGSize(width: UIScreen.main.bounds.width, height: contentHeight)