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

8
我使用以下代码来滚动到UICollectionView的顶部:
scrollView.scrollRectToVisible(CGRect(origin: .zero, size: CGSize(width: 1, height: 1)), animated: true)

然而,在iOS 11和12上,当prefersLargeTitle设置为true时,scrollView只会滚动到顶部,而不会显示UINavigationBar的大标题。

以下是其外观: enter image description here

我想要实现的效果:

enter image description here


你的标题没有变大吗? - dahiya_boy
确切地说,当滚动视图滚动到顶部时,它不会变大。尽管如此,我可以通过手动拖动滚动视图将其恢复。 - Richard Topchii
我认为这是一个属性,在程序上你无法处理它。从开发者的角度来看,你只需要禁用此属性,当用户向上或向下滚动时,标题将相应地放大。 - dahiya_boy
4个回答

7

不要使用任何“神奇值”(如当前接受的答案中的-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)
    }
}

被接受的答案(-64)对于特定问题有效,但是这个答案适用于多种不同的情况。我认为这是最佳答案。 - Dylan
我的viewSafeAreaInsetsDidChange没有被调用,所以它不起作用。 - ChikabuZ

6

它按照设计的方式工作,您正在滚动到位置 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)

1
最好使用-64,尽管从adjustedContentInset.top派生的值更大且滚动视图会“过度滚动”。 - Richard Topchii
这是在Xamarin.iOS中的工作解决方案,所以谢谢你!有关于UICollectionView的任何解决方案吗?因为我似乎遇到了同样的问题。 - Amir Hajiha

2
似乎使用负内容偏移是可行的方法。
我非常喜欢Demosthese的想法,可以跟踪最大的顶部插入量。但是,这种方法存在问题。有时无法显示大标题,例如当iPhone处于横向模式时。
如果在设备旋转到横向后使用此方法,则表格的偏移量将过大,因为导航栏中未显示大标题。
改进此技术的方法是仅在导航栏可以显示大标题时考虑biggestTopSafeAreaInset。现在的问题是要了解何时导航栏可以显示大标题。我对不同设备进行了一些测试,似乎当垂直大小类别紧凑时不显示大标题。
因此,可以通过以下方式改进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方法,则大标题将不会显示。 幸运的是,这种情况不应经常发生。


1
这段内容与编程有关:“这里已经很晚了,但我有我的版本。自iOS 11以来,在滚动视图上有`adjustedContentInset`。然而,它只反映UI的当前状态,因此,如果未显示大型导航标题,则不会将其考虑在内。因此,我的解决方案是进行几次额外的调用,以使系统考虑大标题大小并将其计算到`adjustedContentInset`中:”。
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 时:

  1. 获取当前偏移量以便再次应用它(对于实现动画很重要)
  2. 滚动到当前计算出的 adjustedContentInset,再加上一些因为大标题没有在计算 adjustedContentInset 时被考虑进去
  3. 现在系统考虑了大标题,所以获取当前包含其大小的 adjustedContentInset 并将其存储到一个常量中,在最后一步中使用
  4. 不带动画地滚回原始偏移量,这样就不会注意到任何视觉变化
  5. 滚动到之前计算出的 adjustedContentInset,这次使用动画来实现所需的滚动效果

!animated 时:

  1. 不带动画地滚动到 adjustedContentInset 加上一些。此时系统将考虑大标题,因此...
  2. 滚动到当前包含大标题的 adjustedContentInset

有点像黑客技巧,但确实有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接