UIScrollView + LargeTitle (iOS 11) - 滚动到顶部并显示大标题

UIScrollView + LargeTitle (iOS 11) - scroll to top and reveal the large title

我使用以下代码滚动到 UICollectionView 的顶部:

scrollView.scrollRectToVisible(CGRect(origin: .zero, size: CGSize(width: 1, height: 1)), animated: true)

但是,在 iOS 11 和 12 上,scrollView 仅滚动到顶部,而不会显示 UINavigationBar 的大标题(当 prefersLargeTitle 已设置为 true.)

这是它的样子:

我想要达到的结果:

它按设计工作,您正在滚动到位置 y = 0,将您的 controller 分配给 UIScrollView 委托并打印出滚动偏移量:

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
    print(scrollView.contentOffset)
}

您将看到何时显示大标题并且您移动滚动视图 a 但是它跳回到大标题它不会打印 (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)

您不想使用任何 'magic values'(如当前接受的答案中的 -64)。这些可能会改变(另外,-64 无论如何都不正确)。

更好的解决办法是观察SafeAreaInsets的变化,保存最大的top inset。然后在 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)
    }
}

看来使用负内容偏移是可行的方法。

我真的很喜欢 Demosthese 跟踪最大顶部插图的想法。 但是,这种方法存在一个问题。 有时无法显示大标题,例如,当 iPhone 处于横向模式时。

如果在设备旋转到横向后使用此方法,则 table 的偏移量会太大,因为大标题不会显示在导航栏中。

该技术的一个改进是仅当导航栏可以显示大标题时才考虑biggestTopSafeAreaInset。 现在的问题是了解导航栏何时可以显示大标题。 我在不同的设备上做了一些测试,当垂直尺寸 class 很紧凑时,似乎不会显示大标题。

所以,Demosthese解决方案可以这样改进:

class 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)
        }
    }
}

还有一种情况会导致滚动后大标题不显示

如果用户:

  1. 在横向模式下旋转设备打开应用程序。
  2. 滚动视图。
  3. 纵向旋转设备。

此时biggestTopSafeAreaInset还没有找到最大值,如果调用scrollToTop方法则不会显示大标题。 幸运的是,这种情况不应该经常发生。