不要使用任何“神奇值”(如当前接受的答案中的-64)。这些值可能会更改(此外,-64也不正确)。
更好的解决方案是观察SafeAreaInsets的变化并保存最大的顶部插入。然后在setContentOffset方法中使用此值。像这样:
class CollectioViewController: UIViewController {
var biggestTopSafeAreaInset: CGFloat = 0
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
self.biggestTopSafeAreaInset = max(ui.safeAreaInsets.top, biggestTopSafeAreaInset)
}
func scrollToTop(animated: Bool) {
ui.scrollView.setContentOffset(CGPoint(x: 0, y: -biggestTopSafeAreaInset), animated: animated)
}
}
viewSafeAreaInsetsDidChange
没有被调用,所以它不起作用。 - ChikabuZ它按照设计的方式工作,您正在滚动到位置 y = 0
,将您的控制器
指定为UIScrollView
代理并打印滚动偏移量:
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
print(scrollView.contentOffset)
}
当大标题显示时,您向下滚动视图并跳回到大标题时,它不会打印(0.0, 0.0)
,而是(0.0, -64.0)
或(0.0, -116.0)
- 这与scrollView.adjustedContentInset
的值相同,因此如果您想向上滚动并显示大标题,应该这样做:
scrollView.scrollRectToVisible(CGRect(x: 0, y: -64, width: 1, height: 1), animated: true)
-64
,尽管从adjustedContentInset.top
派生的值更大且滚动视图会“过度滚动”。 - Richard Topchiiclass TableViewController: UITableViewController {
var biggestTopSafeAreaInset: CGFloat = 0
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
self.biggestTopSafeAreaInset = max(view.safeAreaInsets.top, biggestTopSafeAreaInset)
}
func scrollToTop(animated: Bool) {
if traitCollection.verticalSizeClass == .compact {
tableView.setContentOffset(CGPoint(x: 0, y: -view.safeAreaInsets.top), animated: animated)
} else {
tableView.setContentOffset(CGPoint(x: 0, y: -biggestTopSafeAreaInset), animated: animated)
}
}
}
在滚动后,仍有可能出现大标题不显示的情况。
如果用户:
此时,biggestTopSafeAreaInset还没有机会找到最大值,如果调用scrollToTop方法,则大标题将不会显示。 幸运的是,这种情况不应经常发生。
extension UIScrollView {
func scrollToTop(animated: Bool = true) {
if animated {
// 1
let currentOffset = contentOffset
// 2
setContentOffset(CGPoint(x: 0, y: -adjustedContentInset.top - 1), animated: false)
// 3
let newAdjustedContentInset = adjustedContentInset
// 4
setContentOffset(currentOffset, animated: false)
// 5
setContentOffset(CGPoint(x: 0, y: -newAdjustedContentInset.top), animated: true)
} else {
// 1
setContentOffset(CGPoint(x: 0, y: -adjustedContentInset.top - 1), animated: false)
// 2
setContentOffset(CGPoint(x: 0, y: -adjustedContentInset.top), animated: false)
}
}
}
这里是正在发生的事情:
当 animated
时:
adjustedContentInset
,再加上一些因为大标题没有在计算 adjustedContentInset
时被考虑进去adjustedContentInset
并将其存储到一个常量中,在最后一步中使用adjustedContentInset
,这次使用动画来实现所需的滚动效果当 !animated
时:
adjustedContentInset
加上一些。此时系统将考虑大标题,因此...adjustedContentInset
有点像黑客技巧,但确实有效。