当使用 iOS 14.1 SDK 构建时,UIScrollView 会忽略 iOS 12 上的 frameLayoutGuide 约束
UIScrollView ignores frameLayoutGuide constraints on iOS 12 when built with iOS 14.1 SDK
当 运行 在 iOS 12 上时,我只是浪费了一个小时看起来像 iOS 14 SDK 回归,所以我想我会 post 这个以防万一它可以帮助他人。
总结:当我们像往常一样用 frameLayoutGuide
固定 UIScrollView
的框架时,这会中断 iOS 12 当滚动视图包含在另一个视图中时(在我的例子中是 child viewcontroller,不确定这是否重要)。
设置:
查看设置,所有视图都使用自动布局:
UIView "scrollviewContainerView" [fixed/unchanging size]
|
\- UIScrollView "scrollView"
|
\ UIView "contentView"
导致症状的约束:
- scrollview 的
frameLayoutGuide
固定到 scrollviewContainerView 的 top/bottom/leading/trailing
- contentView 已固定到滚动视图
contentLayoutGuide
的 top/bottom/leading/trailing
- 因为这是一个垂直滚动视图:contentView 的
widthAnchor
固定到 scrollView 的 frameLayoutGuide
症状:在iOS14,一切正常。在 iOS 12 滚动时,UIScrollView
本身的大小发生变化,似乎忽略了 frameLayoutGuide
约束(即使它们仍然存在:调试时我看不出有任何区别查看 iOS 12 的约束结果集和 iOS 14 之间的层次结构。这让事情看起来非常非常破碎。
帮助理解UIScrollView
、Frame Layout Guide
和Content Layout Guide
...
Frame Layout Guide
和 Content Layout Guide
是在 iOS 11 中引入的,部分是为了消除框架与内容的歧义,以及允许添加“non-scrolling”滚动视图的元素。
对于以下每个示例,我都像这样添加和限制滚动视图:
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
并使用滚动视图的实际 frame[=48= 来定义“可滚动内容” ](其 .frameLayoutGuide
)来定义堆栈视图的宽度。
此外,假设我想要一个 UI 元素 - 例如图像视图 - 在 滚动视图中,但我不希望它滚动? “旧方法”需要将图像视图添加为覆盖在滚动视图顶部的同级视图。
但是现在,通过使用 .frameLayoutGuide
,我可以将其添加为滚动视图中的“non-scrolling”元素:
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
的图像视图将保留在原位。
当 运行 在 iOS 12 上时,我只是浪费了一个小时看起来像 iOS 14 SDK 回归,所以我想我会 post 这个以防万一它可以帮助他人。
总结:当我们像往常一样用 frameLayoutGuide
固定 UIScrollView
的框架时,这会中断 iOS 12 当滚动视图包含在另一个视图中时(在我的例子中是 child viewcontroller,不确定这是否重要)。
设置: 查看设置,所有视图都使用自动布局:
UIView "scrollviewContainerView" [fixed/unchanging size]
|
\- UIScrollView "scrollView"
|
\ UIView "contentView"
导致症状的约束:
- scrollview 的
frameLayoutGuide
固定到 scrollviewContainerView 的 top/bottom/leading/trailing
- contentView 已固定到滚动视图
contentLayoutGuide
的 top/bottom/leading/trailing
- 因为这是一个垂直滚动视图:contentView 的
widthAnchor
固定到 scrollView 的frameLayoutGuide
症状:在iOS14,一切正常。在 iOS 12 滚动时,UIScrollView
本身的大小发生变化,似乎忽略了 frameLayoutGuide
约束(即使它们仍然存在:调试时我看不出有任何区别查看 iOS 12 的约束结果集和 iOS 14 之间的层次结构。这让事情看起来非常非常破碎。
帮助理解UIScrollView
、Frame Layout Guide
和Content Layout Guide
...
Frame Layout Guide
和 Content Layout Guide
是在 iOS 11 中引入的,部分是为了消除框架与内容的歧义,以及允许添加“non-scrolling”滚动视图的元素。
对于以下每个示例,我都像这样添加和限制滚动视图:
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
并使用滚动视图的实际 frame[=48= 来定义“可滚动内容” ](其 .frameLayoutGuide
)来定义堆栈视图的宽度。
此外,假设我想要一个 UI 元素 - 例如图像视图 - 在 滚动视图中,但我不希望它滚动? “旧方法”需要将图像视图添加为覆盖在滚动视图顶部的同级视图。
但是现在,通过使用 .frameLayoutGuide
,我可以将其添加为滚动视图中的“non-scrolling”元素:
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
的图像视图将保留在原位。