在 iOS 14 上运行时,我只是浪费了一个小时,看起来像 iOS 12 SDK 回归,所以我想我会发布这个以防它帮助其他人。
摘要:当我们像往常一样用frameLayoutGuide
固定UIScrollView
的框架时,当滚动视图包含在另一个视图中时,这在 iOS 12 上会中断(在我的情况下,在子视图控制器中,不确定这是否重要)。
设置: 视图设置,所有视图都使用自动布局:
UIView "scrollviewContainerView" [fixed/unchanging size]
|
- UIScrollView "scrollView"
|
UIView "contentView"
导致症状的约束:
- 滚动视图的
frameLayoutGuide
固定到滚动视图的顶部/底部/前导/尾随容器视图 - 内容视图固定到滚动视图
contentLayoutGuide
的顶部/底部/前导/尾随 - 因为这是一个垂直滚动视图:contentView 的
widthAnchor
固定到 scrollView 的frameLayoutGuide
症状:在iOS 14上,一切看起来都很好。在 iOS 12 上滚动时,UIScrollView
本身的大小会发生变化,似乎忽略了frameLayoutGuide
约束(即使它们仍然存在:在调试 iOS 12 生成的约束集与 iOS 14 之间的视图层次结构时,我看不到任何区别)。这使得事情看起来非常非常破碎。
为了帮助理解UIScrollView
,Frame Layout Guide
和Content Layout Guide
...
iOS 11 中引入了Frame Layout Guide
和Content Layout Guide
,部分原因是为了消除框架与内容的歧义,并允许在滚动视图中添加"非滚动"元素。
对于以下每个示例,我将像这样添加和约束滚动视图:
let scrollView: UIScrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// constrain scrollView 20-pts on each side
scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
])
// so we can see the scroll view's frame
scrollView.backgroundColor = .red
现在,考虑添加内容的"旧"方式:
// call a func to get a vertical stack view with 30 labels
let stackView = createStackView()
scrollView.addSubview(stackView)
NSLayoutConstraint.activate([
// old method
// constrain stack view to scroll view itself
// this defines the "scrollable" content
// stack view Top / Leading / Trailing / Bottom to scroll view
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0.0),
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 0.0),
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: 0.0),
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0.0),
// we want vertical scrolling, so we want our content to be only as wide as
// the scroll view itself
stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: 0.0),
])
相当简单,虽然有点模棱两可。我是否通过约束其底部锚点使堆栈视图仅与滚动视图一样高?如果它是任何其他UIView
的子视图,这就是我所期望的!
因此,请考虑"新"的执行方式:
// call a func to get a vertical stack view with 30 labels
let stackView = createStackView()
scrollView.addSubview(stackView)
let contentG = scrollView.contentLayoutGuide
let frameG = scrollView.frameLayoutGuide
NSLayoutConstraint.activate([
// constrain stack view to scroll view's Content Layout Guide
// this defines the "scrollable" content
// stack view Top / Leading / Trailing / Bottom to scroll view's Content Layout Guide
stackView.topAnchor.constraint(equalTo: contentG.topAnchor, constant: 0.0),
stackView.leadingAnchor.constraint(equalTo: contentG.leadingAnchor, constant: 0.0),
stackView.trailingAnchor.constraint(equalTo: contentG.trailingAnchor, constant: 0.0),
stackView.bottomAnchor.constraint(equalTo: contentG.bottomAnchor, constant: 0.0),
// we want vertical scrolling, so we want our content to be only as wide as
// the scroll view's Frame Layout Guide
stackView.widthAnchor.constraint(equalTo: frameG.widthAnchor, constant: 0.0),
])
现在,很明显,我正在使用堆栈视图来定义"可滚动内容",方法是将其约束到.contentLayoutGuide
并使用滚动视图的实际框架(其.frameLayoutGuide
)来定义堆栈视图的宽度。
此外,假设我希望一个 UI 元素(例如图像视图)位于滚动视图中,但我不希望它滚动?"旧方法"需要将图像视图添加为覆盖在滚动视图顶部的同级视图。
但是现在,通过使用.frameLayoutGuide
,我可以将其添加为滚动视图中的"非滚动"元素:
guard let img = UIImage(named: "scrollSample") else {
fatalError("could not load image")
}
let imgView = UIImageView(image: img)
imgView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(imgView)
NSLayoutConstraint.activate([
// constrain image view to the Frame Layout Guide
// at top-right, with 20-pts Top / Trailing "padding"
imgView.topAnchor.constraint(equalTo: frameG.topAnchor, constant: 20.0),
imgView.trailingAnchor.constraint(equalTo: frameG.trailingAnchor, constant: -20.0),
// 120 x 120 pts
imgView.widthAnchor.constraint(equalToConstant: 120.0),
imgView.heightAnchor.constraint(equalToConstant: 120.0),
])
现在,我限制在 scrollView.contentLayoutGuide
的任何元素都将滚动,但约束到 scrollView.frameLayoutGuide
的图像视图将保留在原位。