当前位置: 首页 > 面试题库 >

我的带有自动布局约束的Swift 4 UIScrollView无法滚动

那铭
2023-03-14
问题内容

在将视图添加到我的应用程序之前,我创建了一个小演示场来使其工作。

我有一个滚动视图,该视图将包含许多按钮来水平滚动。我知道这些按钮需要进入滚动视图内的容器视图,并且也已经创建了。我最初使用自动布局约束创建所有这些内容,但是现在尝试使用常量来确保内容视图大于滚动视图。但是,按钮仍然不会滚动…我错过了什么吗?滚动视图不适用于自动布局吗?

我也在iPad上以编程方式进行了所有这些操作,因此不幸的是,无法使用Interface Builder解决方案

这是完整的代码:

import UIKit
import PlaygroundSupport

class FilterViewController: UIViewController {
    var filterView: UIView!
    var scrollView: UIScrollView!
    var containerView: UIView!

    override func loadView() {
        filterView = UIView()
        view = filterView
        view.backgroundColor = #colorLiteral(red: 0.909803926944733, green: 0.47843137383461, blue: 0.643137276172638, alpha: 1.0)

        scrollView = UIScrollView()
        scrollView.backgroundColor = #colorLiteral(red: 0.474509805440903, green: 0.839215695858002, blue: 0.976470589637756, alpha: 1.0)
        view.addSubview(scrollView)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.topAnchor.constraint(equalTo:view.topAnchor, constant:40).isActive = true
        scrollView.leadingAnchor.constraint(equalTo:view.leadingAnchor).isActive = true
        scrollView.widthAnchor.constraint(equalTo:view.widthAnchor).isActive = true
        scrollView.heightAnchor.constraint(equalToConstant: 200).isActive = true
        scrollView.isScrollEnabled = true

        containerView = UIView()
        containerView.backgroundColor = #colorLiteral(red: 0.176470592617989, green: 0.498039215803146, blue: 0.756862759590149, alpha: 1.0)
        scrollView.addSubview(containerView)
        containerView.frame = CGRect(x: 0, y: 0, width: 1080, height: 200)
    }

    class Buttons{
        let button = UIButton()
        init (titleText : String){
            button.backgroundColor = #colorLiteral(red: 0.976470589637756, green: 0.850980401039124, blue: 0.549019634723663, alpha: 1.0)
            button.setTitle(titleText, for: .normal)
            button.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let b1 = Buttons(titleText: "one")
        let b2 = Buttons(titleText: "two")
        let b3 = Buttons(titleText: "three")
        let b4 = Buttons(titleText: "four")
        let b5 = Buttons(titleText: "five")
        let buttonArray = [b1,b2,b3,b4,b5]
        var startPoint : CGFloat = 0.0
        for btn in buttonArray {
            let theBtn = btn.button
            containerView.addSubview(theBtn)
            theBtn.frame = CGRect(x: startPoint, y: 0, width: 200, height: 200)
            startPoint += 220
        }

    }
}

let filterViewController = FilterViewController()
PlaygroundPage.current.liveView = filterViewController

谢谢vacawama!这是具有所有自动布局约束的完整的(正在运行的)迷你项目:

import UIKit
import PlaygroundSupport

class FilterViewController: UIViewController {
    var filterView: UIView!
    var scrollView: UIScrollView!
    var containerView: UIView!

    override func loadView() {
        filterView = UIView()
        view = filterView
        view.backgroundColor = #colorLiteral(red: 0.909803926944733, green: 0.47843137383461, blue: 0.643137276172638, alpha: 1.0)

        scrollView = UIScrollView()
        scrollView.backgroundColor = #colorLiteral(red: 0.474509805440903, green: 0.839215695858002, blue: 0.976470589637756, alpha: 1.0)
        view.addSubview(scrollView)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.topAnchor.constraint(equalTo:view.topAnchor, constant:40).isActive = true
        scrollView.leadingAnchor.constraint(equalTo:view.leadingAnchor).isActive = true
        scrollView.widthAnchor.constraint(equalTo:view.widthAnchor).isActive = true
        scrollView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.25).isActive = true
        scrollView.isScrollEnabled = true

        containerView = UIView()
        containerView.backgroundColor = #colorLiteral(red: 0.176470592617989, green: 0.498039215803146, blue: 0.756862759590149, alpha: 1.0)
        scrollView.addSubview(containerView)
        containerView.translatesAutoresizingMaskIntoConstraints = false
        containerView.topAnchor.constraint(equalTo:scrollView.topAnchor).isActive = true
        containerView.leadingAnchor.constraint(equalTo:scrollView.leadingAnchor).isActive = true
        containerView.trailingAnchor.constraint(equalTo:scrollView.trailingAnchor).isActive = true
        containerView.bottomAnchor.constraint(equalTo:scrollView.bottomAnchor).isActive = true
        containerView.heightAnchor.constraint(equalTo: scrollView.heightAnchor).isActive = true



    }

    class Buttons{
        let button = UIButton()
        init (titleText : String){
            button.backgroundColor = #colorLiteral(red: 0.976470589637756, green: 0.850980401039124, blue: 0.549019634723663, alpha: 1.0)
            button.setTitle(titleText, for: .normal)
            //button.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let b1 = Buttons(titleText: "one")
        let b2 = Buttons(titleText: "two")
        let b3 = Buttons(titleText: "three")
        let b4 = Buttons(titleText: "four")
        let b5 = Buttons(titleText: "five")
        let buttonArray = [b1,b2,b3,b4,b5]
        var startPoint = containerView.leadingAnchor
        for btn in buttonArray {
            let theBtn = btn.button
            containerView.addSubview(theBtn)
            theBtn.translatesAutoresizingMaskIntoConstraints = false
            theBtn.leadingAnchor.constraint(equalTo:startPoint, constant:20).isActive = true
            theBtn.topAnchor.constraint(equalTo:containerView.topAnchor).isActive = true
            theBtn.bottomAnchor.constraint(equalTo:containerView.bottomAnchor).isActive = true
            theBtn.widthAnchor.constraint(equalTo: theBtn.heightAnchor).isActive = true
            startPoint = theBtn.trailingAnchor
            containerView.widthAnchor.constraint(equalTo: theBtn.widthAnchor, multiplier:CGFloat(buttonArray.count), constant: CGFloat(buttonArray.count * 20)).isActive = true
        }
    }
}

let filterViewController = FilterViewController()
PlaygroundPage.current.liveView = filterViewController

问题答案:

您可以使用“ 自动布局”来实现 。该 秘密
是的边缘约束containerView到的边缘scrollView。它不是直观的,但是约束对象的边缘containerView并不会设置大小,只是确保内容的大小随scrollView增长而containerView增长。通过将的宽度约束设置为containerView一个常数,该常数大于的宽度scrollView,内容将水平滚动。

注意:
scrollView这种方式配置时,请勿设置contentSizescrollView。在contentSize将计算为你 自动布局
,这将是相等的大小containerView。重要的是要确保containerView约束的大小完全指定的大小。

这是我为了使其工作而进行的更改:

containerView = UIView()
containerView.backgroundColor = #colorLiteral(red: 0.176470592617989, green: 0.498039215803146, blue: 0.756862759590149, alpha: 1.0)
scrollView.addSubview(containerView)
//containerView.frame = CGRect(x: 0, y: 0, width: 1080, height: 200)
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.topAnchor.constraint(equalTo:scrollView.topAnchor).isActive = true
containerView.leadingAnchor.constraint(equalTo:scrollView.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo:scrollView.trailingAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo:scrollView.bottomAnchor).isActive = true
containerView.heightAnchor.constraint(equalToConstant: 200).isActive = true
containerView.widthAnchor.constraint(equalToConstant: 1080).isActive = true

为什么我的内容没有滚动?

要使其滚动,containerView 必须
大于scrollView。您的错误是您设置了约束,使得的containerView宽度和高度与相同scrollView,这就是为什么内容不滚动的原因。

如果您想使其水平滚动,则的宽度containerView必须大于scrollView的宽度。您可以通过以下两种方式之一进行操作:

  1. containerView为大于的宽度指定一个显式的恒定scrollView宽度。

要么

  1. containerView从左到右链接的子视图,最左边的子视图包含在的前沿containerView。完全指定子视图的宽度,并在子视图之间放置距离约束。最右边的子视图必须与的后沿偏移containerView。这样, 自动版式 可以计算的宽度containerView并设置contentSizescrollView

迷你项目:更新

这是您迷你项目的一个版本,它使用一系列受约束的视图来定义containerView的宽度。关键是for循环之后的最终约束,在该循环中viewDidLoad(),最后一个按钮的traceingAnchor(aka
startPoint)连接到了containerViewTrailingAnchor。这样就完成了约束和纽扣链,将的前缘containerView与的后缘连接起来containerView。有了这个,
自动布局 能够计算的宽度containerView和建立contentSizescrollView

import UIKit
import PlaygroundSupport

class FilterViewController: UIViewController {
    var filterView: UIView!
    var scrollView: UIScrollView!
    var containerView: UIView!

    override func loadView() {
        filterView = UIView()
        view = filterView
        view.backgroundColor = #colorLiteral(red: 0.909803926944733, green: 0.47843137383461, blue: 0.643137276172638, alpha: 1.0)

        scrollView = UIScrollView()
        scrollView.backgroundColor = #colorLiteral(red: 0.474509805440903, green: 0.839215695858002, blue: 0.976470589637756, alpha: 1.0)
        view.addSubview(scrollView)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 40).isActive = true
        scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
        scrollView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.25).isActive = true
        scrollView.isScrollEnabled = true

        containerView = UIView()
        containerView.backgroundColor = #colorLiteral(red: 0.176470592617989, green: 0.498039215803146, blue: 0.756862759590149, alpha: 1.0)
        scrollView.addSubview(containerView)
        containerView.translatesAutoresizingMaskIntoConstraints = false

        // This is key:  connect all four edges of the containerView to
        // to the edges of the scrollView
        containerView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
        containerView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
        containerView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
        containerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true

        // Making containerView and scrollView the same height means the
        // content will not scroll vertically
        containerView.heightAnchor.constraint(equalTo: scrollView.heightAnchor).isActive = true    
    }

    class Buttons {
        let button = UIButton()
        init(titleText: String) {
            button.backgroundColor = #colorLiteral(red: 0.976470589637756, green: 0.850980401039124, blue: 0.549019634723663, alpha: 1.0)
            button.setTitle(titleText, for: .normal)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let b1 = Buttons(titleText: "one")
        let b2 = Buttons(titleText: "two")
        let b3 = Buttons(titleText: "three")
        let b4 = Buttons(titleText: "four")
        let b5 = Buttons(titleText: "five")
        let buttonArray = [b1, b2, b3, b4, b5]
        var startPoint = containerView.leadingAnchor
        for btn in buttonArray {
            let theBtn = btn.button
            containerView.addSubview(theBtn)
            theBtn.translatesAutoresizingMaskIntoConstraints = false
            theBtn.leadingAnchor.constraint(equalTo: startPoint, constant: 20).isActive = true
            theBtn.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
            theBtn.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
            theBtn.widthAnchor.constraint(equalTo: theBtn.heightAnchor).isActive = true
            startPoint = theBtn.trailingAnchor
        }
        // Complete the chain of constraints
        containerView.trailingAnchor.constraint(equalTo: startPoint, constant: 20).isActive = true
    }
}

let filterViewController = FilterViewController()
PlaygroundPage.current.liveView = filterViewController


 类似资料:
  • 我有一个观点,我已经以以下方式发出通知。 我在填充视图的视图中有一个UIImageView。 该视图具有以下约束: 水平居中 我想通过翻转视图来设置视图的动画。我尝试了两种方法来做到这一点。 方法1:使用视图转换 这将导致在动画翻转时显示完整图像(图像丢失纵横比填充,看起来像是在视图中而不是视图本身翻转图像)。 方法2:应用层变换 这会导致一个非常糟糕的动画,其中视图控制器的一半被切断。我相信这可

  • 所有包含的视图都对内容视图具有前导和尾随约束,没有问题。包装器视图有一个恒定的高度,即ContentView的顶部约束。集合视图对包装的底部具有顶部约束,TableView对集合的底部具有顶部约束,对内容视图具有底部约束。问题是,如果我没有为CollectionView设置固定的高度,我就有自动布局问题。但我不希望TableView或CollectionView都有固定的高度。有什么想法吗? 编辑

  • 我有一组AL约束来定位一个子vc,它有两个位置,展开和折叠。 我发现,当我添加折叠约束时,带有常量的上锚到下锚约束,当第一次创建vc时,当我激活它时,似乎有额外的间距。似乎是因为当时没有实际的高度。 当我在viewDidLayoutSubviews中添加约束时,额外的间距消失了,约束行为正常。除了现在在动画中的约束之间切换时,切换到展开约束和约束打断时,无法停用折叠的约束。可能是因为在整个过渡动画

  • 我觉得显示/隐藏是一个相当常见的范例,最常见的是,具体取决于业务逻辑。我的问题是,使用自动布局响应隐藏视图的最佳方式是什么,就好像它们的框架是0x0一样。这是一个1-3个功能的动态列表示例。 现在我有一个10px的顶部空间,从按钮到最后一个标签,当标签被隐藏时,它显然不会向上滑动。现在,我创建了这个约束的出口,并根据显示的标签数量修改常量。这显然有点麻烦,因为我使用负常量值将按钮推到隐藏帧上。这也

  • 许多有关自动布局约束动画的教程建议更新约束的常量属性,然后在动画块中调用。 我的处境有点棘手。我有一个包含3个子视图的视图。此超级视图的高度不是固定的-它是按其子视图的高度之和计算的。在某些情况下,我要求这3个子视图中的一个子视图切换其高度(它在0和30之间变化,即我希望平滑地隐藏和显示它)。代码类似于此: 不幸的是,这并不像我预期的那样有效。我可以看到子视图高度的平滑变化,但在为子视图高度约束设

  • 我有一个视图控制器,它有一个UITableView实例,并使用自动布局约束来呈现它。我希望在这个表格视图中有可变的单元格高度。我不想自己计算细胞高度,因为我有一个复杂的细胞内容,由几个标签和图像组成,这些标签和图像可以因细胞而异。我相信可以让自动布局单元格调整自己的大小,使其包含所有子视图(即,在将文本分配给?之后,使用标签的sizeToFit方法)。 我有一个自定义单元格类,它使用自动布局的可视