如何使编程滚动视图容器的高度与其内容的确切大小相同 Swift?

How to make a programatic scrollviews container height the exact size of its content in Swift?

我的问题如下:

contentView的高度比它的子视图高,所以虽然contentView的子视图没有垂直占据整个屏幕,您仍然可以滚动浏览内容。

如何设置 contentView 的高度以匹配其子视图的高度,这样如果屏幕在垂直方向上足够大以适应内容,您不能滚动,但如果屏幕较小或发送键盘信号,则可以垂直滚动?

我知道问题出在 contentView.anchor 中的高度限制,但我不知道如何修复它,而且我一直无法解决找到任何答案。

let scrollView = UIScrollView()
let contentView = UIView()

override func viewDidLoad() {
    super.viewDidLoad()
    
    configureScrollView()
    configureUI()
    
}

func configureScrollView() {
            
    view.addSubview(scrollView)
    scrollView.anchor(top: view.safeAreaLayoutGuide.topAnchor,
                      left: view.leftAnchor,
                      bottom: view.safeAreaLayoutGuide.bottomAnchor,
                      right: view.rightAnchor)
    
    scrollView.addSubview(contentView)
    contentView.anchor(top: scrollView.topAnchor,
                       left: scrollView.leftAnchor,
                       bottom: scrollView.bottomAnchor,
                       right: scrollView.rightAnchor,
                       width: view.frame.size.width,
                       height: view.frame.size.height)
}

func configureUI() {
    
    contentView.addSubview(previewTitleLabel)
    previewTitleLabel.anchor(top: contentView.topAnchor,
                             left: contentView.leftAnchor,
                             right: contentView.rightAnchor,
                             paddingTop: 20,
                             paddingLeft: 20,
                             paddingRight: 20,
                             height: 20)

    contentView.addSubview(previewView)
    previewView.anchor(top: previewTitleLabel.bottomAnchor,
                       left: contentView.leftAnchor,
                       right: contentView.rightAnchor,
                       paddingTop: 20,
                       paddingLeft: 20,
                       paddingRight: 20) // Has dynamic height

    contentView.addSubview(detailsTitleLabel)
    detailsTitleLabel.anchor(top: previewView.bottomAnchor,
                             left: contentView.leftAnchor,
                             right: contentView.rightAnchor,
                             paddingTop: 20,
                             paddingLeft: 20,
                             paddingRight: 20,
                             height: 20)

    contentView.addSubview(descriptionView)
    descriptionView.anchor(top: detailsTitleLabel.bottomAnchor,
                           left: contentView.leftAnchor,
                           right: contentView.rightAnchor,
                           paddingTop: 20,
                           paddingLeft: 20,
                           paddingRight: 20,
                           height: 278)
}

情侣提示:

  1. 浏览 half-dozen 左右的滚动视图教程,了解它们的工作原理。
  2. 完成 half-dozen 左右 auto-layout 教程,以便了解约束的工作原理。
  3. 使用标准约束语法代替您的 .anchor()“助手”,直到您完全理解约束的工作原理。这也让您可以逻辑地“分组”您的约束,正如您将在本示例代码中看到的那样。
  4. 在布局开发期间,为您的 UI 元素提供对比鲜明的背景颜色,以便于查看框架。

因此,示例代码:

class ExampleScrollViewController: UIViewController {

    let scrollView = UIScrollView()
    let contentView = UIView()
    
    let previewTitleLabel = UILabel()
    let previewView = UIView()
    let detailsTitleLabel = UILabel()
    let descriptionView = UIView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        configureScrollView()
        configureUI()
        
        // use some background colors so we can easily see frames
        scrollView.backgroundColor = .red
        contentView.backgroundColor = .blue
        previewTitleLabel.backgroundColor = .cyan
        previewView.backgroundColor = .green
        detailsTitleLabel.backgroundColor = .cyan
        descriptionView.backgroundColor = .green

        previewTitleLabel.text = "Preview Title Label"
        detailsTitleLabel.text = "Details Title Label"
    }
    
    func configureScrollView() {

        contentView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.translatesAutoresizingMaskIntoConstraints = false

        scrollView.addSubview(contentView)
        view.addSubview(scrollView)
        
        // respect safe area
        let g = view.safeAreaLayoutGuide
        
        // reference to scrollView's contentLayoutGuide
        let svContentLG = scrollView.contentLayoutGuide
        
        // reference to scrollView's frameLayoutGuide
        let svFrameLG = scrollView.frameLayoutGuide
        

        NSLayoutConstraint.activate([
            
            // constrain scroll view to full safe area
            scrollView.topAnchor.constraint(equalTo: g.topAnchor),
            scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor),

            // constrain content view to all 4 sides of scroll view's content layout guide
            contentView.topAnchor.constraint(equalTo: svContentLG.topAnchor),
            contentView.leadingAnchor.constraint(equalTo: svContentLG.leadingAnchor),
            contentView.trailingAnchor.constraint(equalTo: svContentLG.trailingAnchor),
            contentView.bottomAnchor.constraint(equalTo: svContentLG.bottomAnchor),
            
            // we want vertical scrolling,
            //  so constrain width of content view to scroll view's frame
            contentView.widthAnchor.constraint(equalTo: svFrameLG.widthAnchor),

        ])

    }
    
    func configureUI() {
        
        [previewTitleLabel, previewView, detailsTitleLabel, descriptionView].forEach {
            [=10=].translatesAutoresizingMaskIntoConstraints = false
            contentView.addSubview([=10=])
        }
        
        NSLayoutConstraint.activate([

            // horizontal constraints
            
            // all 4 subviews will be constrained
            //  Leading and Trailing to the contentView
            //  with 20-pts "padding" on left and right
            previewTitleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20.0),
            previewTitleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20.0),
            previewView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20.0),
            previewView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20.0),
            detailsTitleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20.0),
            detailsTitleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20.0),
            descriptionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20.0),
            descriptionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20.0),

            // vertical spacing constraints
            
            // previewTitleLabel Top 20-pts from contentView Top
            previewTitleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20.0),

            // previewView Top 20-pts from previewTitleLabel Bottom
            previewView.topAnchor.constraint(equalTo: previewTitleLabel.bottomAnchor, constant: 20.0),

            // detailsTitleLabel Top 20-pts from previewView Bottom
            detailsTitleLabel.topAnchor.constraint(equalTo: previewView.bottomAnchor, constant: 20.0),

            // descriptionView Top 20-pts from detailsTitleLabel Bottom
            descriptionView.topAnchor.constraint(equalTo: detailsTitleLabel.bottomAnchor, constant: 20.0),

            // complete the vertical spacing constraints by
            //  constraining descriptionView Bottom 20-pts from contentView Bottom
            descriptionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20.0),

            // vertical height constraints
            
            // previewTitleLabel Height: 20
            previewTitleLabel.heightAnchor.constraint(equalToConstant: 20.0),
            
            // previewView Height: 20
            previewView.heightAnchor.constraint(equalToConstant: 20.0),
            
            // detailsTitleLabel Height: 20
            detailsTitleLabel.heightAnchor.constraint(equalToConstant: 20.0),
            
            // descriptionView Height: 270
            descriptionView.heightAnchor.constraint(equalToConstant: 270.0),

        ])

    }
    
}

结果:

如果我将 previewView 的高度也更改为 270:

previewView.heightAnchor.constraint(equalToConstant: 270.0),

我们得到这个结果:

我们可以向上滚动查看内容的底部: