Ios 带有自动布局约束的Swift 4 UIScrollView未滚动

Ios 带有自动布局约束的Swift 4 UIScrollView未滚动,ios,swift,uiscrollview,swift-playground,programmatically-created,Ios,Swift,Uiscrollview,Swift Playground,Programmatically Created,在将视图添加到我的应用程序之前,我已经创建了一个小的演示游乐场来实现这一点 我有一个滚动视图,它将包含许多按钮,可以水平滚动。我知道这些按钮需要进入滚动视图中的容器视图,并且已经创建了它。我最初使用autolayout约束来创建所有这些,但现在尝试使用常量来确保内容视图比滚动视图大。但是,按钮仍然不会滚动。。。我错过什么了吗?滚动视图不适用于自动布局吗 我在我的iPad上也以编程的方式完成了这一切,所以不幸的是,使用interface builder的解决方案不是一个选项 以下是完整的代码: i

在将视图添加到我的应用程序之前,我已经创建了一个小的演示游乐场来实现这一点

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

我在我的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
谢谢你,瓦卡瓦马! 以下是包含所有自动布局约束的完整(立即工作)小项目:

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
时,您不会设置
scrollView
contentSize
。自动布局将为您计算
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
    的子视图从左到右进行链接,将最左侧的子视图限制在
    containerView
    的前缘。完全指定子视图的宽度,并在子视图之间放置距离约束。最右边的子视图必须与
    containerView
    的后缘有偏移。通过这样做,Auto Layout可以计算
    containerView
    的宽度,并设置
    scrollView
    contentSize

  • 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
    

    小型项目:更新

    这是迷你项目的一个版本,它使用一系列受约束的视图来定义
    容器视图的宽度。键是
    viewDidLoad()
    中的
    for
    循环后的最后一个约束,该循环将最后一个按钮的跟踪定位点(又名
    startPoint
    )连接到
    containerView
    的跟踪定位点。这就完成了连接
    containerView
    前缘和
    containerView
    后缘的约束和按钮链。这样,Auto Layout就能够计算
    containerView
    的宽度,并建立
    scrollView
    contentSize

    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
    

    作为一名新的程序员,这可能会让我感到很复杂。您也可以使用UICollectionView来实现这一点。感谢vivekDas,我也尝试创建了一个集合视图,但一开始就遇到了问题。你知道有哪些教程会告诉我如何以编程方式创建一个我可以学习的集合吗?我找到的都是使用interface builder的……你可以通过这个url:@JohnClevesy:请不要把你的答案放在你的问题中。请把它换成一个答案。这样,其他人可以给你投票支持你的解决方案。对不起,我误读了一些东西。还有一件事。关于您的第一个建议,即“为containerView指定一个大于scrollView宽度的显式常量宽度”\uu您不也需要指定scrollView的
    contentSize
    ?那么什么时候需要指定
    contentSize
    将是第三种方法?@Honey,如果不使用自动布局,您可以设置
    contentSize
    ,但整个答案都是关于使用自动布局使其工作。添加每个按钮后,您需要几分钟的时间才能完全理解,将
    startPoint
    更新为上次添加按钮的
    trailingAnchor
    。然后将该值用于“下一步”按钮的
    leadingAnchor