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

Posted

技术标签:

【中文标题】在 Swift 中以编程方式创建垂直 UIScrollView【英文标题】:Create a vertical UIScrollView programmatically in Swift 【发布时间】:2015-05-19 14:07:46 【问题描述】:

我一直在寻找(工作)教程甚至是使用 UIScrollView 以编程方式垂直滚动的示例应用程序。使用故事板的教程太多了,让我不知所措。

我浏览了苹果的文档,他们的“指南”仍然没有一个可靠的例子或提示从哪里开始。到目前为止,我所做的尝试是做以下一些事情。

在课堂上直接让我的视图成为滚动视图

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

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

我没有收到任何错误。

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

这是灾难的截图,试图让我的视图可滚动的原因。

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

按照 cmets 中的建议,我尝试添加 scrollview 作为 self.view 的子视图,而不是 self.view = self.scrollview,但没有积极的结果。

添加

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

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

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

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

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

任何想法为什么这不起作用?

【问题讨论】:

你有没有尝试过不替换当前视图,而是将scrollView添加为子视图? 我马上试试! 将其添加为子视图,并将我所有的“self.view.addsubview(..) 更改为 self.scrollView.addSubView(...),与我的约束相同。但它看起来仍然很糟糕. 让我更新一下我的帖子,用一张灾难的图片。 self.scrollView.contentSize = CGSize(width:2000, height: 5678) 放入viewDidLayoutSubviews 并让我知道会发生什么...& 也放入viewDidAppear... 我认为问题出在self.view = scollView 应该是self.view.addSubview(scollView) 不确定什么是快速语法但添加子视图... 【参考方案1】:

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
    ()

希望对您有所帮助!

【讨论】:

清晰简洁。谢谢!【参考方案2】:

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

self.view = scollView

应该是self.view.addSubview(scollView)

然后在滚动视图中添加所有标签、按钮等,然后给出内容大小。

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

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

【讨论】:

有一点。由于我仍然遇到布局问题,但这不是我最初的问题。我应该为此创建一个新问题吗? @MarkL :您将其标记为已接受是个好点。我不会赞成我接受的答案...仅在它对您有帮助且不被接受为答案的情况下才赞成... viewDidLayoutSubviews 对我来说非常有用。谢谢! @FahimParkar 你在这里写 contentSize 静态的,但我们的要求是动态的【参考方案3】:

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

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

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

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

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

除非你的视图有一些intrinsicContentSize,那么此时如果你运行代码,你会得到一个运行时错误提示:ScrollView Content size is ambiguous强>。继续阅读以了解有关intrinsicContentSize 的更多信息。

解决办法是什么?

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

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

在所有涉及的视图上将 translatesAutoresizingMaskIntoConstraints 设置为 NO。 使用外部到滚动视图的约束来定位和调整滚动视图的大小。 使用约束来布置滚动视图中的子视图,确保约束绑定到滚动视图的所有四个边缘,并且不要依赖滚动视图来获取它们的大小。

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

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


换个说法:

ScrollView 需要:

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

你看,操作系统可以计算出 而无需查看标签的周围环境。这就是我在不依赖滚动视图的情况下计算大小的意思


另请参阅here 的文档。

【讨论】:

以上是关于在 Swift 中以编程方式创建垂直 UIScrollView的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 中以编程方式将视图添加到 stackview

UIScrollView 在 Swift 中以编程方式

在 Swift 中以编程方式创建 NSTextField

在 Swift 中以编程方式创建 UITableViewController

如何在 Swift 中以编程方式创建 SplitViewController?

使用 Cocoa 在 Swift 4.0 中以编程方式创建复选框的标准方法是啥?