在 Swift 中以编程方式创建垂直 UIScrollView

Create a vertical UIScrollView programmatically in Swift

我一直在寻找一个(工作)教程,甚至是一个使用 UIScrollView 以编程方式垂直滚动的示例应用程序。 storyboard 教程一大堆,看得我一头雾水

我查看了苹果的文档,他们的“指南”仍然没有关于从哪里开始的可靠示例或提示。到目前为止,我尝试的是执行以下一些操作。

直接在 class

中使我的视图成为滚动视图
let scrollView = UIScrollView(frame: UIScreen.mainScreen().bounds)

然后在我的 viewDidLoad 函数中将其分配给视图

self.view = scollView

正在尝试更改内容大小。

self.scrollView.contentSize = CGSize(width:2000, height: 5678)

尝试使用

启用滚动
scrollView.scrollEnabled = true

我能找到的关于以编程方式执行此操作的最后建议

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    scrollView.frame = view.bounds
}

目前我还没有尝试开始将我的对象添加到滚动视图,(我不需要缩放,只需要垂直滚动),但我还没有设法让任何东西工作:(事实上, 运行 添加了这些内容的应用程序只会导致 UI 问题,屏幕奇怪地向上移动,并且不适合我屏幕的整个宽度?我尝试修复使框架边界等于宽度的问题,但仍然没用

我没有收到任何错误。

我很乐意看到一个可以垂直滚动的示例 viewcontroller!或者任何帮助将不胜感激!

这是灾难的屏幕截图,试图使我的视图可滚动。

(我将滚动视图的背景设为红色,以查看它是否正确显示。它似乎是这样。但我无法滚动到任何地方

正如评论中所建议的那样,我没有做 self.view = self.scrollview,而是尝试将滚动视图添加为 self.view 的子视图,但没有得到积极的结果。

添加

scrollView.contentSize = CGSize(width:2000, height: 5678)

到 viewDidLayoutSubviews,正如下面评论中所建议的那样,使我的视图可滚动!

但是由于某种原因,我的布局看起来仍然一团糟(在我使其可滚动之前,它看起来应该是这样)。

这是我的 topBar(蓝色的)的示例约束,它应该占据整个水平 space。

self.scrollView.addConstraints(
        NSLayoutConstraint.constraintsWithVisualFormat(
            "H:|[topBar]|", options: nil, metrics: nil, views: viewsDictionary))

知道为什么这不起作用吗?

严格来说我觉得问题出在下面。

self.view = scollView

应该是self.view.addSubview(scollView)

然后在scrollview中添加所有标签,按钮等,然后给内容大小。

内容大小是告诉 scrollview 滚动的参数。

self.scrollView.contentSize = CGSize(width:2000, height: 5678)放在viewDidLayoutSubviews里面。

Swift 4.2

我制作了使用自动布局滚动堆栈视图的简单而完整的示例。

所有视图都在代码中,不需要故事板。

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(scrollView)
        scrollView.addSubview(scrollViewContainer)
        scrollViewContainer.addArrangedSubview(redView)
        scrollViewContainer.addArrangedSubview(blueView)
        scrollViewContainer.addArrangedSubview(greenView)

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

        scrollViewContainer.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
        scrollViewContainer.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
        scrollViewContainer.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
        scrollViewContainer.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
        // this is important for scrolling
        scrollViewContainer.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
    }

    let scrollView: UIScrollView = {
        let scrollView = UIScrollView()

        scrollView.translatesAutoresizingMaskIntoConstraints = false
        return scrollView
    }()

    let scrollViewContainer: UIStackView = {
        let view = UIStackView()

        view.axis = .vertical
        view.spacing = 10

        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    let redView: UIView = {
        let view = UIView()
        view.heightAnchor.constraint(equalToConstant: 500).isActive = true
        view.backgroundColor = .red
        return view
    }()

    let blueView: UIView = {
        let view = UIView()
        view.heightAnchor.constraint(equalToConstant: 200).isActive = true
        view.backgroundColor = .blue
        return view
    }()

    let greenView: UIView = {
        let view = UIView()
        view.heightAnchor.constraint(equalToConstant: 1200).isActive = true
        view.backgroundColor = .green
        return view
    }()
}

希望对您有所帮助!

感谢 Tom Insam 帮我整理了这个答案:

您需要像往常一样添加所有约束,即

  • 为滚动视图及其父视图添加约束。
  • 为滚动视图的内容添加约束。

然后暂停。了解这一点,即使您的 scrollView 的 frame 已知为例如400 * 600,其内容的大小仍然未知。它可以是 400 * 6000 或 400 * 300 或任何其他尺寸。

没有其他基于边缘(前导、尾随、左、右、上、下、边距)的约束可用于计算滚动视图的内容大小。

除非你的观点有一些intrinsicContentSize,那么此时如果你运行代码,你会得到一个运行 -时间错误说:ScrollView 内容大小不明确。继续阅读以了解有关 intrinsicContentSize 的更多信息。

有什么解决办法?

https://developer.apple.com/library/archive/technotes/tn2154/_index.html

要使用纯自动布局方法,请执行以下操作:

  • Set translatesAutoresizingMaskIntoConstraints to NO on all views involved.
  • Position and size your scroll view with constraints external to the scroll view.
  • Use constraints to lay out the subviews within the scroll view, being sure that the constraints tie to all four edges of the scroll view and do not rely on the scroll view to get their size.

我加粗的部分是我回答的全部重点。您需要设置一个 独立于边 的 (horizontal/vertical) 约束。因为否则它将依赖滚动视图来获取它的大小。

相反,您需要明确设置宽度、高度或附加到轴中心的约束。如果视图具有 intrinsicContentSize,则不需要第三部分,例如标签或文本视图可以根据字体、字符长度和换行符计算它们的内容大小。要深入了解 intrinsicContentSize,请参阅 here


换句话说:

ScrollView 需要:

  • 对其父视图的外部约束
  • 内容的内部限制
  • 边到边/链接的子视图以其高度的方式(如果可以计算垂直滚动)。对于布局引擎,将 contentSize 设置为 1000 的高度与链接多个子视图没有任何不同,其中所有内容的高度都可以计算为 1000。例如你有两个标签,它们有 400 行。每条线占 2 点,两个标签之间有 100 点线 space 和 100 点 space 等于 1000 ( 400 * 2 + 100 + 100) 点。

你看,OS不用查看标签的周围环境就可以计算出。这就是我在不依赖 scrollview

的情况下计算大小的意思

另请参阅 here 中的文档。