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

Posted

技术标签:

【中文标题】我的带有自动布局约束的 Swift 4 UIScrollView 没有滚动【英文标题】:My Swift 4 UIScrollView with autolayout constraints is not scrolling 【发布时间】:2018-08-04 17:39:23 【问题描述】:

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

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

我也在我的 iPad 上以编程方式完成这一切,所以很遗憾,界面生成器的解决方案不是一个选项...

这里是完整的代码:

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

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

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

【问题讨论】:

作为一个新程序员,这可能会让我很复杂。你也可以使用 UICollectionView 来实现这一点。 感谢 vivekDas,我也尝试制作收藏视图,但一开始就遇到了问题。您是否知道任何教程可以向我展示如何以编程方式创建我可以学习的集合?我发现的都在使用界面生成器... 你可以通过这个网址:raywenderlich.com/136159/… @JohnClevesy:请不要将您的解决方案放入您的问题中。请将其移至答案中。这样,其他人就可以为您的解决方案投票。 【参考方案1】:

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

注意:这样配置scrollView时,不要设置scrollViewcontentSizecontentSize 将由 Auto Layout 为您计算,它将等于 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 的宽度。您可以通过以下两种方式之一执行此操作:

    containerView 指定一个大于scrollView 宽度的显式常量宽度。

    从左到右链接containerView 的子视图,最左边的被包含在containerView 的前沿。完全指定子视图的宽度,并在子视图之间放置距离约束。最右边的子视图必须与containerView 的后沿有一个偏移量。通过这样做,Auto Layout 可以计算containerView 的宽度并设置scrollViewcontentSize


小项目:更新

这是您的迷你项目的一个版本,它使用一系列受约束的视图来定义containerView 的宽度。关键是viewDidLoad()for 循环之后的最终约束,它将最后一个按钮的 trailingAnchor(又名startPoint)连接到containerView 的 trailingAnchor。这样就完成了连接containerView 的前缘和containerView 的后缘的约束和按钮链。有了这个,Auto Layout 能够计算containerView 的宽度,并建立scrollViewcontentSize

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

【讨论】:

对不起,我误读了一些东西。还有一件事。关于您的第一个建议_为大于滚动视图宽度的容器视图指定显式恒定宽度_您是否还需要指定滚动视图的contentSize 那么你什么时候需要指定contentSize 将是第三种方式呢? @Honey,如果您不使用 Auto Layout,则设置 contentSize,但整个答案是关于使用 Auto Layout 来制作它工作。 This answer is being discussed on meta. 花了几分钟才完全掌握在添加每个按钮后,您将startPoint 更新为最后添加按钮的trailingAnchor。然后将该值用于下一个按钮的leadingAnchor

以上是关于我的带有自动布局约束的 Swift 4 UIScrollView 没有滚动的主要内容,如果未能解决你的问题,请参考以下文章

隐藏视图时,自动布局中的 Swift 约束不会改变

Swift UIView 自动布局和约束

Swift 程序化自动布局约束的问题

如何在 ios swift 中以编程方式创建自动布局等宽约束?

如何在ios swift中以编程方式创建自动布局等宽度约束?

有啥方法可以在 Swift 中以编程方式获取所有应用的自动布局约束