使用带有最小内容顶部锚点的 UIScrollView 会导致视觉故障
Using UIScrollView with a minimum content top anchor causes visual glitch
我有一个滚动视图,其中有一个内容视图。我将滚动视图的顶部锚点设置在图像底部的正上方。我将内容视图的顶部锚点设置为实际上位于图像的底部。这样你就可以下拉内容并显示到图像的底部,而不能进一步下拉内容视图。但是,这会导致内容跳转。
这是我的代码:
class HomeParallaxScrollViewController: UIViewController {
private let topImageView = UIImageView(image: UIImage(named: "cat"))
private let contentView = UIView()
private let scrollView = UIScrollView()
private let label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .gray
topImageView.contentMode = .scaleAspectFill
contentView.backgroundColor = .white
label.text = "SOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT"
label.textColor = .black
label.numberOfLines = 0
[contentView, label, topImageView, scrollView].forEach { [=10=].translatesAutoresizingMaskIntoConstraints = false }
scrollView.addSubview(contentView)
contentView.addSubview(label)
view.addSubview(topImageView)
view.addSubview(scrollView)
NSLayoutConstraint.activate([
topImageView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
topImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
topImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
topImageView.heightAnchor.constraint(equalToConstant: 200),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor),
scrollView.topAnchor.constraint(equalTo: topImageView.bottomAnchor, constant: -30),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
contentView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
contentView.topAnchor.constraint(lessThanOrEqualTo: topImageView.bottomAnchor), //This is what's causing the glitch
label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
label.topAnchor.constraint(equalTo: contentView.topAnchor),
label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])
}
}
这是正在发生的事情:
尝试添加另一个顶部约束——尤其是添加到滚动视图之外的元素——是个坏主意,而且如您所见,这是行不通的。我确定您注意到正在生成自动布局冲突消息。
一种方法是实现 scrollViewDidScroll
委托函数:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// limit drag-down in the scroll view to the overlap size
scrollView.contentOffset.y = max(scrollView.contentOffset.y, -30)
}
当用户向下拖动滚动时,它会在 30 点处停止。
这是您的示例,稍作修改 -- 我没有您的 .plBackgroundLightGray
或 .PLSemiboldFont
,我为顶部图像视图添加了图像加载 -- 但这应该 运行原样:
// conform to UIScrollViewDelegate
class HomeParallaxScrollViewController: UIViewController, UIScrollViewDelegate {
private let topImageView = UIImageView(image: UIImage(named: "cat"))
private let contentView = UIView()
private let scrollView = UIScrollView()
private let label = UILabel()
// this will be the "overlap" of the scroll view and top image view
private var scrollOverlap: CGFloat = 30.0
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// limit drag-down in the scroll view to scrollOverlap points
scrollView.contentOffset.y = max(scrollView.contentOffset.y, -scrollOverlap)
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .lightGray // .plBackgroundLightGray
topImageView.contentMode = .scaleAspectFill
if let img = UIImage(named: "background") {
topImageView.image = img
}
contentView.backgroundColor = .white
label.text = "SOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT"
label.font = UIFont.boldSystemFont(ofSize: 16) // .PLSemiboldFont(size: 16)
label.textColor = .black
label.numberOfLines = 0
[contentView, label, topImageView, scrollView].forEach { [=11=].translatesAutoresizingMaskIntoConstraints = false }
scrollView.addSubview(contentView)
contentView.addSubview(label)
view.addSubview(topImageView)
view.addSubview(scrollView)
NSLayoutConstraint.activate([
topImageView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
topImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
topImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
topImageView.heightAnchor.constraint(equalToConstant: 200),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor),
scrollView.topAnchor.constraint(equalTo: topImageView.bottomAnchor, constant: scrollOverlap),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
contentView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
// nope, not a good idea -- will cause constraint conflicts
//contentView.topAnchor.constraint(lessThanOrEqualTo: topImageView.bottomAnchor), //This is what's causing the glitch
label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
label.topAnchor.constraint(equalTo: contentView.topAnchor),
label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])
// set delegate to self
scrollView.delegate = self
}
}
我有一个滚动视图,其中有一个内容视图。我将滚动视图的顶部锚点设置在图像底部的正上方。我将内容视图的顶部锚点设置为实际上位于图像的底部。这样你就可以下拉内容并显示到图像的底部,而不能进一步下拉内容视图。但是,这会导致内容跳转。
这是我的代码:
class HomeParallaxScrollViewController: UIViewController {
private let topImageView = UIImageView(image: UIImage(named: "cat"))
private let contentView = UIView()
private let scrollView = UIScrollView()
private let label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .gray
topImageView.contentMode = .scaleAspectFill
contentView.backgroundColor = .white
label.text = "SOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT"
label.textColor = .black
label.numberOfLines = 0
[contentView, label, topImageView, scrollView].forEach { [=10=].translatesAutoresizingMaskIntoConstraints = false }
scrollView.addSubview(contentView)
contentView.addSubview(label)
view.addSubview(topImageView)
view.addSubview(scrollView)
NSLayoutConstraint.activate([
topImageView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
topImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
topImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
topImageView.heightAnchor.constraint(equalToConstant: 200),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor),
scrollView.topAnchor.constraint(equalTo: topImageView.bottomAnchor, constant: -30),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
contentView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
contentView.topAnchor.constraint(lessThanOrEqualTo: topImageView.bottomAnchor), //This is what's causing the glitch
label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
label.topAnchor.constraint(equalTo: contentView.topAnchor),
label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])
}
}
这是正在发生的事情:
尝试添加另一个顶部约束——尤其是添加到滚动视图之外的元素——是个坏主意,而且如您所见,这是行不通的。我确定您注意到正在生成自动布局冲突消息。
一种方法是实现 scrollViewDidScroll
委托函数:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// limit drag-down in the scroll view to the overlap size
scrollView.contentOffset.y = max(scrollView.contentOffset.y, -30)
}
当用户向下拖动滚动时,它会在 30 点处停止。
这是您的示例,稍作修改 -- 我没有您的 .plBackgroundLightGray
或 .PLSemiboldFont
,我为顶部图像视图添加了图像加载 -- 但这应该 运行原样:
// conform to UIScrollViewDelegate
class HomeParallaxScrollViewController: UIViewController, UIScrollViewDelegate {
private let topImageView = UIImageView(image: UIImage(named: "cat"))
private let contentView = UIView()
private let scrollView = UIScrollView()
private let label = UILabel()
// this will be the "overlap" of the scroll view and top image view
private var scrollOverlap: CGFloat = 30.0
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// limit drag-down in the scroll view to scrollOverlap points
scrollView.contentOffset.y = max(scrollView.contentOffset.y, -scrollOverlap)
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .lightGray // .plBackgroundLightGray
topImageView.contentMode = .scaleAspectFill
if let img = UIImage(named: "background") {
topImageView.image = img
}
contentView.backgroundColor = .white
label.text = "SOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT"
label.font = UIFont.boldSystemFont(ofSize: 16) // .PLSemiboldFont(size: 16)
label.textColor = .black
label.numberOfLines = 0
[contentView, label, topImageView, scrollView].forEach { [=11=].translatesAutoresizingMaskIntoConstraints = false }
scrollView.addSubview(contentView)
contentView.addSubview(label)
view.addSubview(topImageView)
view.addSubview(scrollView)
NSLayoutConstraint.activate([
topImageView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
topImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
topImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
topImageView.heightAnchor.constraint(equalToConstant: 200),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor),
scrollView.topAnchor.constraint(equalTo: topImageView.bottomAnchor, constant: scrollOverlap),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
contentView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
// nope, not a good idea -- will cause constraint conflicts
//contentView.topAnchor.constraint(lessThanOrEqualTo: topImageView.bottomAnchor), //This is what's causing the glitch
label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
label.topAnchor.constraint(equalTo: contentView.topAnchor),
label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])
// set delegate to self
scrollView.delegate = self
}
}