斯威夫特:我无法使自动布局约束适用于我的 UIScrollView

Posted

技术标签:

【中文标题】斯威夫特:我无法使自动布局约束适用于我的 UIScrollView【英文标题】:Swift: I'm not able to make autolayout constraints work for my UIScrollView 【发布时间】:2017-06-08 10:03:45 【问题描述】:

我正在尝试制作一个简单的布局,当视图太多而无法在屏幕上显示时,它应该垂直滚动。

这就是我目前所拥有的:

我创建了一个滚动视图和一个类似的容器视图

let mainScrollView: UIScrollView = 

    let scrollView = UIScrollView()

    scrollView.isUserInteractionEnabled = true
    scrollView.translatesAutoresizingMaskIntoConstraints = false

    return scrollView
()

let containerView: UIView = 

    let view = UIView()

    view.backgroundColor = .lightGray
    view.translatesAutoresizingMaskIntoConstraints = false

    return view
()

然后我将滚动视图添加到主视图,将容器视图添加到滚动视图,并将几个标签添加到容器视图

view.addSubview(mainScrollView)
mainScrollView.addSubview(containerView)

containerView.addSubview(firstLabel)
containerView.addSubview(secondLabel)

我将滚动视图固定到主视图,将容器视图固定到滚动视图。之后我开始像这样在容器视图中添加标签

mainScrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
mainScrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
mainScrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
mainScrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true

containerView.topAnchor.constraint(equalTo: mainScrollView.layoutMarginsGuide.topAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: mainScrollView.layoutMarginsGuide.bottomAnchor).isActive = true
containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true

firstLabel.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
firstLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 30).isActive = true
firstLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true
firstLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true

secondLabel.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
secondLabel.topAnchor.constraint(equalTo: firstLabel.bottomAnchor, constant: 750).isActive = true
secondLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true
secondLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true

问题是由于某种原因,滚动视图无法计算它的高度并且它不会垂直滚动。第二个标签仍然不可见,因为它在屏幕上太“低”了。

我尝试将容器视图的底部约束设置为第二个标签的底部,但老实说我无处可去。

我错过了什么?如何设置约束以使滚动视图使用自动布局滚动(不设置容器视图的特定高度)?

【问题讨论】:

【参考方案1】:

Scrollview 向任一方向滚动。因此,添加到其中的任何视图都需要一些额外的约束来让 Scrollview 计算其内容大小。

您已将容器视图固定到滚动视图。如果您正在查看像表格视图这样的垂直滚动视图,那么您还需要将滚动的宽度设置为容器视图,以便它可以计算宽度。

接下来是高度,它会根据容器视图中添加的 UI 元素自动计算。确保所有标签在容器视图和容器视图顶部都具有前导、尾随和垂直间距。这将使滚动视图知道所需的高度。

    self.scrollView.translatesAutoresizingMaskIntoConstraints = false
    self.contentView.translatesAutoresizingMaskIntoConstraints = false
    self.label1.translatesAutoresizingMaskIntoConstraints = false
    self.label2.translatesAutoresizingMaskIntoConstraints = false
    self.label3.translatesAutoresizingMaskIntoConstraints = false

    self.contentView.addSubview(self.label1)
    self.contentView.addSubview(self.label2)
    self.contentView.addSubview(self.label3)

    self.scrollView.addSubview(self.contentView)
    self.view.addSubview(self.scrollView)

    NSLayoutConstraint.activate([
        self.scrollView.topAnchor.constraint(equalTo: self.view.topAnchor),
        self.scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
        self.scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
        self.scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor)
    ])

    NSLayoutConstraint.activate([
        self.contentView.topAnchor.constraint(equalTo: self.scrollView.topAnchor),
        self.contentView.trailingAnchor.constraint(equalTo: self.scrollView.trailingAnchor),
        self.contentView.bottomAnchor.constraint(equalTo: self.scrollView.bottomAnchor),
        self.contentView.leadingAnchor.constraint(equalTo: self.scrollView.leadingAnchor),
        self.contentView.widthAnchor.constraint(equalTo: self.scrollView.widthAnchor)
    ])

    NSLayoutConstraint.activate([
        self.label1.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
        self.label2.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
        self.label3.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
        self.label1.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor),
        self.label2.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor),
        self.label3.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor)
    ])

    NSLayoutConstraint.activate([
        self.label1.topAnchor.constraint(equalTo: self.contentView.topAnchor),
        self.label2.topAnchor.constraint(equalTo: self.label1.bottomAnchor),
        self.label3.topAnchor.constraint(equalTo: self.label2.bottomAnchor),
        self.label3.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor)
    ])

并且在运行时,它也不会抱怨任何约束问题。水平滚动也可以这样做。

【讨论】:

非常感谢,您给了我正确的提示,我解决了问题。我提交了一个答案,以供将来可能会发现此问题的读者参考。 @Luca 没问题!即将用代码更新。 :) 也请查看NSLayoutConstraint.activate([])。我发现这很方便。 哇,真的好用!肯定会用,谢谢建议【参考方案2】:

非常感谢@GoodSp33d,你为我指明了正确的方向。

我像你说的那样添加了宽度约束:

containerView.widthAnchor.constraint(equalTo: mainScrollView.widthAnchor).isActive = true

而且关键还是要更新底部约束:

containerView.bottomAnchor.constraint(equalTo: secondLabel.bottomAnchor).isActive = true

这是完整的工作代码:

mainScrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
mainScrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
mainScrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
mainScrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true

containerView.topAnchor.constraint(equalTo: mainScrollView.topAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: mainScrollView.trailingAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: mainScrollView.bottomAnchor).isActive = true
containerView.leadingAnchor.constraint(equalTo: mainScrollView.leadingAnchor).isActive = true
containerView.widthAnchor.constraint(equalTo: mainScrollView.widthAnchor).isActive = true

firstLabel.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
firstLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 30).isActive = true
firstLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true
firstLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true

secondLabel.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
secondLabel.topAnchor.constraint(equalTo: firstLabel.bottomAnchor, constant: 550).isActive = true
secondLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true
secondLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true

containerView.bottomAnchor.constraint(equalTo: secondLabel.bottomAnchor).isActive = true

感谢您的帮助。

【讨论】:

以上是关于斯威夫特:我无法使自动布局约束适用于我的 UIScrollView的主要内容,如果未能解决你的问题,请参考以下文章

在 Interface Builder 中删除自动布局(约束)

使用自动布局使 UIView 全屏

使 LINQ 扩展方法适用于我的班级

自动布局视图不会对手机的旋转做出反应

自动布局无法同时满足约束[重复]

使用自动布局约束时,UIView 无法通过 UIpangesture 顺利拖动