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