Ios 编程自动布局不计算UIScrollView的高度

Ios 编程自动布局不计算UIScrollView的高度,ios,swift,mobile,autolayout,programmatically-created,Ios,Swift,Mobile,Autolayout,Programmatically Created,我有一个aUIViewController,它有一个标题区域。在标题元素的底部是一行UIButtons。在这些按钮下面,我放置了一个UIScrollView,其中嵌入了一个名为contentContainer的UIView。在该容器中有许多ui标签和ui视图样式为分隔符 整个ViewController、所有子视图及其关联的自动布局约束都是以编程方式构造的 除了UIScrollView没有垂直滚动之外,一切似乎都很好。如果我显式地给contentContainer子视图一个带有自动布局高度约束的

我有一个a
UIViewController
,它有一个标题区域。在标题元素的底部是一行UIButtons。在这些按钮下面,我放置了一个
UIScrollView
,其中嵌入了一个名为
contentContainer
UIView
。在该容器中有许多
ui标签
ui视图
样式为分隔符

整个ViewController、所有子视图及其关联的自动布局约束都是以编程方式构造的

除了UIScrollView没有垂直滚动之外,一切似乎都很好。如果我显式地给
contentContainer
子视图一个带有自动布局高度约束的大小,它就可以工作了

这让我觉得contentContainer的高度并没有被精确计算出来

如何构建每个UI元素的示例:

let divider1: UIView = {
    let divider = UIView()
    divider.backgroundColor = Colors.white10
    divider.translatesAutoresizingMaskIntoConstraints = false

    return divider
}()
我的视图加载:

override func viewDidLoad() {
    super.viewDidLoad()

    self.view.addSubview(buttonClose)
    self.view.addSubview(labelPlaceTitle)
    self.view.addSubview(buttonShare)
    self.view.addSubview(buttonRoute)
    self.view.addSubview(buttonDelete)
    self.view.addSubview(scrollView)

    scrollView.addSubview(contentContainer)

    contentContainer.addSubview(divider1)
    contentContainer.addSubview(labelAddressLabel)
    contentContainer.addSubview(labelAddressActual)
    contentContainer.addSubview(divider2)
    contentContainer.addSubview(labelDateLabel)
    contentContainer.addSubview(labelDateActual)
    contentContainer.addSubview(divider3)
    contentContainer.addSubview(labelNoteLabel)
    contentContainer.addSubview(labelNoteActual)
    contentContainer.addSubview(buttonAddNote)
    contentContainer.addSubview(divider4)

    setupLayout()
}
我的自动布局约束:

private func setupLayout() {
    buttonClose.topAnchor.constraint(equalTo: view.topAnchor, constant: 16).isActive = true
    buttonClose.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16).isActive = true
    buttonClose.widthAnchor.constraint(equalToConstant: 28).isActive = true
    buttonClose.heightAnchor.constraint(equalToConstant: 28).isActive = true

    labelPlaceTitle.topAnchor.constraint(equalTo: view.topAnchor, constant: 48).isActive = true
    labelPlaceTitle.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 24).isActive = true
    labelPlaceTitle.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -24).isActive = true

    buttonShare.topAnchor.constraint(equalTo: labelPlaceTitle.bottomAnchor, constant: 16).isActive = true
    buttonShare.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 24).isActive = true
    buttonShare.widthAnchor.constraint(equalToConstant: 48).isActive = true
    buttonShare.heightAnchor.constraint(equalToConstant: 48).isActive = true

    buttonRoute.topAnchor.constraint(equalTo: labelPlaceTitle.bottomAnchor, constant: 16).isActive = true
    buttonRoute.leftAnchor.constraint(equalTo: buttonShare.rightAnchor, constant: 8).isActive = true
    buttonRoute.widthAnchor.constraint(equalToConstant: 48).isActive = true
    buttonRoute.heightAnchor.constraint(equalToConstant: 48).isActive = true

    buttonDelete.topAnchor.constraint(equalTo: labelPlaceTitle.bottomAnchor, constant: 16).isActive = true
    buttonDelete.leftAnchor.constraint(equalTo: buttonRoute.rightAnchor, constant: 8).isActive = true
    buttonDelete.widthAnchor.constraint(equalToConstant: 48).isActive = true
    buttonDelete.heightAnchor.constraint(equalToConstant: 48).isActive = true

    scrollView.topAnchor.constraint(equalTo: buttonRoute.bottomAnchor, constant: 32).isActive = true
    scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
    scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
    scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true

    contentContainer.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0).isActive = true
    contentContainer.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 0).isActive = true
    contentContainer.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 0).isActive = true
    contentContainer.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0).isActive = true
    contentContainer.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor).isActive = true
    contentContainer.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor).isActive = true
    contentContainer.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true

    divider1.topAnchor.constraint(equalTo: contentContainer.topAnchor, constant: 24).isActive = true
    divider1.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    divider1.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
    divider1.heightAnchor.constraint(equalToConstant: 1).isActive = true

    labelAddressLabel.topAnchor.constraint(equalTo: divider1.bottomAnchor, constant: 12).isActive = true
    labelAddressLabel.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    labelAddressLabel.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true

    labelAddressActual.topAnchor.constraint(equalTo: labelAddressLabel.bottomAnchor, constant: 4).isActive = true
    labelAddressActual.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    labelAddressActual.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true

    divider2.topAnchor.constraint(equalTo: labelAddressActual.bottomAnchor, constant: 12).isActive = true
    divider2.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    divider2.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
    divider2.heightAnchor.constraint(equalToConstant: 1).isActive = true

    labelDateLabel.topAnchor.constraint(equalTo: divider2.bottomAnchor, constant: 12).isActive = true
    labelDateLabel.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    labelDateLabel.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true

    labelDateActual.topAnchor.constraint(equalTo: labelDateLabel.bottomAnchor, constant: 4).isActive = true
    labelDateActual.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    labelDateActual.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true

    divider3.topAnchor.constraint(equalTo: labelDateActual.bottomAnchor, constant: 12).isActive = true
    divider3.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    divider3.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
    divider3.heightAnchor.constraint(equalToConstant: 1).isActive = true

    labelNoteLabel.topAnchor.constraint(equalTo: divider3.bottomAnchor, constant: 12).isActive = true
    labelNoteLabel.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    labelNoteLabel.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true

    labelNoteActual.topAnchor.constraint(equalTo: labelNoteLabel.bottomAnchor, constant: 4).isActive = true
    labelNoteActual.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    labelNoteActual.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true

    buttonAddNote.topAnchor.constraint(equalTo: labelNoteActual.bottomAnchor, constant: 12).isActive = true
    buttonAddNote.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    buttonAddNote.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
    buttonAddNote.heightAnchor.constraint(equalToConstant: 32).isActive = true

    divider4.topAnchor.constraint(equalTo: buttonAddNote.bottomAnchor, constant: 12).isActive = true
    divider4.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    divider4.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
    divider4.heightAnchor.constraint(equalToConstant: 1).isActive = true
}
contentContainer.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor).isActive = true
contentContainer.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor).isActive = true
}

以及UI的一个快照,其中表的浅灰色部分表示
contentContainer


感谢您的帮助。

为了计算
contentContainer
的高度,自动布局需要从
contentContainer
顶部到底部的一系列子视图。在您的例子中,除了您最底部的视图未连接到
contentContainer
的底部之外,您似乎拥有该链

添加一个约束,将
divider4
的底部连接到
contentContainer
的底部。这将允许自动布局计算
contentContainer
的大小

此外,不应设置这些约束:

private func setupLayout() {
    buttonClose.topAnchor.constraint(equalTo: view.topAnchor, constant: 16).isActive = true
    buttonClose.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16).isActive = true
    buttonClose.widthAnchor.constraint(equalToConstant: 28).isActive = true
    buttonClose.heightAnchor.constraint(equalToConstant: 28).isActive = true

    labelPlaceTitle.topAnchor.constraint(equalTo: view.topAnchor, constant: 48).isActive = true
    labelPlaceTitle.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 24).isActive = true
    labelPlaceTitle.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -24).isActive = true

    buttonShare.topAnchor.constraint(equalTo: labelPlaceTitle.bottomAnchor, constant: 16).isActive = true
    buttonShare.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 24).isActive = true
    buttonShare.widthAnchor.constraint(equalToConstant: 48).isActive = true
    buttonShare.heightAnchor.constraint(equalToConstant: 48).isActive = true

    buttonRoute.topAnchor.constraint(equalTo: labelPlaceTitle.bottomAnchor, constant: 16).isActive = true
    buttonRoute.leftAnchor.constraint(equalTo: buttonShare.rightAnchor, constant: 8).isActive = true
    buttonRoute.widthAnchor.constraint(equalToConstant: 48).isActive = true
    buttonRoute.heightAnchor.constraint(equalToConstant: 48).isActive = true

    buttonDelete.topAnchor.constraint(equalTo: labelPlaceTitle.bottomAnchor, constant: 16).isActive = true
    buttonDelete.leftAnchor.constraint(equalTo: buttonRoute.rightAnchor, constant: 8).isActive = true
    buttonDelete.widthAnchor.constraint(equalToConstant: 48).isActive = true
    buttonDelete.heightAnchor.constraint(equalToConstant: 48).isActive = true

    scrollView.topAnchor.constraint(equalTo: buttonRoute.bottomAnchor, constant: 32).isActive = true
    scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
    scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
    scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true

    contentContainer.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0).isActive = true
    contentContainer.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 0).isActive = true
    contentContainer.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 0).isActive = true
    contentContainer.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0).isActive = true
    contentContainer.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor).isActive = true
    contentContainer.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor).isActive = true
    contentContainer.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true

    divider1.topAnchor.constraint(equalTo: contentContainer.topAnchor, constant: 24).isActive = true
    divider1.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    divider1.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
    divider1.heightAnchor.constraint(equalToConstant: 1).isActive = true

    labelAddressLabel.topAnchor.constraint(equalTo: divider1.bottomAnchor, constant: 12).isActive = true
    labelAddressLabel.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    labelAddressLabel.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true

    labelAddressActual.topAnchor.constraint(equalTo: labelAddressLabel.bottomAnchor, constant: 4).isActive = true
    labelAddressActual.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    labelAddressActual.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true

    divider2.topAnchor.constraint(equalTo: labelAddressActual.bottomAnchor, constant: 12).isActive = true
    divider2.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    divider2.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
    divider2.heightAnchor.constraint(equalToConstant: 1).isActive = true

    labelDateLabel.topAnchor.constraint(equalTo: divider2.bottomAnchor, constant: 12).isActive = true
    labelDateLabel.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    labelDateLabel.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true

    labelDateActual.topAnchor.constraint(equalTo: labelDateLabel.bottomAnchor, constant: 4).isActive = true
    labelDateActual.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    labelDateActual.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true

    divider3.topAnchor.constraint(equalTo: labelDateActual.bottomAnchor, constant: 12).isActive = true
    divider3.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    divider3.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
    divider3.heightAnchor.constraint(equalToConstant: 1).isActive = true

    labelNoteLabel.topAnchor.constraint(equalTo: divider3.bottomAnchor, constant: 12).isActive = true
    labelNoteLabel.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    labelNoteLabel.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true

    labelNoteActual.topAnchor.constraint(equalTo: labelNoteLabel.bottomAnchor, constant: 4).isActive = true
    labelNoteActual.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    labelNoteActual.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true

    buttonAddNote.topAnchor.constraint(equalTo: labelNoteActual.bottomAnchor, constant: 12).isActive = true
    buttonAddNote.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    buttonAddNote.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
    buttonAddNote.heightAnchor.constraint(equalToConstant: 32).isActive = true

    divider4.topAnchor.constraint(equalTo: buttonAddNote.bottomAnchor, constant: 12).isActive = true
    divider4.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
    divider4.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
    divider4.heightAnchor.constraint(equalToConstant: 1).isActive = true
}
contentContainer.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor).isActive = true
contentContainer.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor).isActive = true

谢谢你的提示!我添加了
divider4.bottomAnchor.constraint(equalTo:contentContainer.bottomAnchor,常量:0)。isActive=true
,不幸的是它仍然不起作用。想知道为什么吗?我想你不想要
contentContainer.centerYAnchor
contentContainer.centerXAnchor
约束。试着把这些评论出来。啊,你说得对!非常感谢你的帮助。按照上面的指定,将
divider4
bottomAnchor添加到
contentContainer
,然后移除centerX和centerY锚解决了这个问题。ScrollView的框架可能小于内容大小,这是一种自然行为,请仔细想想。如果希望帧大小等于contentSize,则应自行设置。如果您想要自动计算高度,您应该创建一个自定义contentView来包装所有子视图以实现这一点。在我正在测试的实例中,内容比
UIScrollView
frame要大—我认为
contentContainer
是您所指的自定义视图,我希望它能根据自动布局约束进行计算。好的,如果你想要自定义视图,可以自动计算它的高度。有三件事需要确认。1.顶部约束应与顶部子视图的顶部相关。2.底部约束应与底部子视图的底部相关。3.所有子视图约束应明确无误,且高度应可由自动布局系统解决。:)