UIScrollView 在使用 iOS 14.1 SDK 构建时忽略 iOS 12 上的 frameLayoutGui



在 iOS 14 上运行时,我只是浪费了一个小时,看起来像 iOS 12 SDK 回归,所以我想我会发布这个以防它帮助其他人。

摘要:当我们像往常一样用frameLayoutGuide固定UIScrollView的框架时,当滚动视图包含在另一个视图中时,这在 iOS 12 上会中断(在我的情况下,在子视图控制器中,不确定这是否重要)。

设置: 视图设置,所有视图都使用自动布局:

UIView "scrollviewContainerView" [fixed/unchanging size]
|
- UIScrollView "scrollView"
|
 UIView "contentView"

导致症状的约束:

  1. 滚动视图的frameLayoutGuide固定到滚动视图的顶部/底部/前导/尾随容器视图
  2. 内容视图固定到滚动视图contentLayoutGuide的顶部/底部/前导/尾随
  3. 因为这是一个垂直滚动视图:contentView 的widthAnchor固定到 scrollView 的frameLayoutGuide

症状:在iOS 14上,一切看起来都很好。在 iOS 12 上滚动时,UIScrollView本身的大小会发生变化,似乎忽略了frameLayoutGuide约束(即使它们仍然存在:在调试 iOS 12 生成的约束集与 iOS 14 之间的视图层次结构时,我看不到任何区别)。这使得事情看起来非常非常破碎。

为了帮助理解UIScrollViewFrame Layout GuideContent Layout Guide...

iOS 11 中引入了Frame Layout GuideContent 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的图像视图将保留在原位。

最新更新