I met a strange problem, the subviews width is always 0 when I add it as subview of scrollView, but it works when I give the subview a specific number such as make.width.equalTo(200)
.
I was wondering if it is because the scrollView cannot get the right width, but I check the hierarchy of screen it shows width is 393. Why the subviews suddenly cannot get right width.
Here is my demo:
class ViewController: UIViewController {
private lazy var scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.delegate = self
scrollView.alwaysBounceVertical = true
scrollView.contentInsetAdjustmentBehavior = .always
scrollView.backgroundColor = .white
return scrollView
}()
private lazy var viewOne: UIView = {
let view = UIView()
view.backgroundColor = .blue
return view
}()
private lazy var viewTwo: UIView = {
let view = UIView()
view.backgroundColor = .yellow
view.alpha = 0.5
return view
}()
private lazy var viewThree: UIView = {
let view = UIView()
view.backgroundColor = .red
view.alpha = 0.5
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupUI()
}
private func setupUI() {
view.backgroundColor = .white
view.addSubview(scrollView)
scrollView.addSubview(viewOne)
viewOne.backgroundColor = .blue
scrollView.addSubview(viewTwo)
scrollView.addSubview(viewThree)
scrollView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
viewOne.snp.makeConstraints { make in
make.top.equalToSuperview()
make.left.right.equalToSuperview()
make.height.equalTo(200)
}
viewTwo.snp.makeConstraints { make in
make.top.equalTo(viewOne.snp.bottom).offset(12)
make.left.right.equalToSuperview()
make.height.equalTo(200)
}
viewThree.snp.makeConstraints { make in
make.top.equalTo(viewTwo.snp.bottom).offset(12)
make.left.right.equalToSuperview()
make.height.equalTo(200)
make.bottom.equalToSuperview()
}
}
}
extension ViewController: UIScrollViewDelegate {
}
Starting with iOS 11
, a UIScrollView
has 3 "frames" ...
.frame
itself.contentLayoutGuide
.frameLayoutGuide
This was done because constraining subviews to the scroll view was ambiguous.
Constraints to the .contentLayoutGuide
control the actual .contentSize
. So, if you constrain a subview leading and trailing to the .contentLayoutGuide
, and give that subview a width: 2000
, the scroll view will scroll 2000-points horizontally.
If you want to size your subview(s) relative to the size of the scroll view's frame, make use of the .frameLayoutGuide
.
Take a look at the changes here:
private func setupUI() {
view.backgroundColor = .white
view.addSubview(scrollView)
scrollView.addSubview(viewOne)
viewOne.backgroundColor = .blue
scrollView.addSubview(viewTwo)
scrollView.addSubview(viewThree)
scrollView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
viewOne.snp.makeConstraints { make in
make.top.equalTo(scrollView.contentLayoutGuide)
make.left.right.equalTo(scrollView.contentLayoutGuide)
make.height.equalTo(200)
// width equal to scrollView's frameLayoutGuide width
make.width.equalTo(scrollView.frameLayoutGuide)
}
viewTwo.snp.makeConstraints { make in
make.top.equalTo(viewOne.snp.bottom).offset(12)
make.left.right.equalTo(scrollView.contentLayoutGuide)
make.height.equalTo(200)
// width equal to scrollView's frameLayoutGuide width
make.width.equalTo(scrollView.frameLayoutGuide)
}
viewThree.snp.makeConstraints { make in
make.top.equalTo(viewTwo.snp.bottom).offset(12)
make.left.right.equalTo(scrollView.contentLayoutGuide)
make.height.equalTo(200)
make.bottom.equalTo(scrollView.contentLayoutGuide)
// width equal to scrollView's frameLayoutGuide width
make.width.equalTo(scrollView.frameLayoutGuide)
}
}