iOS 11 使用大标题无法正常滚动到顶部

iOS 11 scroll to top when using large titles doesn't work properly

当使用大标题并点击状态栏滚动到 UIScrollViewUITableView 的顶部时(可能还有 UICollectionView,还没有测试过)总是这样有点过分了。

我在我的 TableView 上启用了刷新,当点击状态栏时,它显示为这样并保持这种状态,直到我点击屏幕。

我在另一个 ViewController 中有一个 ScrollView,如果我点击那里的状态栏,它也会滚动得有点远,使导航栏太高。当我点击某处或滚动一点点时,这也 returns 正常。

正常:

点击状态栏后:

这也只发生在我激活大标题时,使用普通标题一切正常。

有什么解决办法吗?

如何重新创建:

  1. 创建一个新项目,其中包含一个导航控制器和一个 UIViewController,里面有一个 TableView
  2. 将导航控制器设置为更喜欢大标题。关闭半透明。在 UIViewController
  3. 上设置标题
  4. TableView 上设置约束以固定到 ViewController
  5. 的边缘
  6. ViewController
  7. 中为 TableView 创建出口
  8. 实施委托并设置行数,例如 100
  9. 启动应用程序
  10. 向下滚动,使大标题变成普通标题
  11. 点击状态栏使 tableView 滚动到顶部

现在标题不在应有的位置,如果您现在向上或向下滚动一点点,它会快速回到正常位置。

ViewController代码:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.delegate = self
        tableView.dataSource = self
    }

}

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 100
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath)

        return cell
    }

}

好的,所以我找到了问题发生的原因,但没有找到在那种情况下如何解决它。

如果您使用大标题和导航栏半透明设置为关闭的 UITableViewController,则会出现问题。 当您重新打开半透明时,问题就消失了。

如果您在普通的 UIViewController 中使用 TableView,问题总是会发生。

编辑

事实证明,如果您使用的是半透明导航栏,设置 "extendedLayoutIncludesOpaqueBars = true" 可以解决问题![​​=11=]

类似问题:UIRefreshControl() in iOS 11 Glitchy effect

经过几个小时的测试,我找到了一个适用于 UIViewController 的解决方案。

在带有 UITableView 的 UIViewController 中,您应该:

  • 在 viewDidLoad 中设置 extendedLayoutIncludesOpaqueBars = true
  • 在情节提要中,将 tableView 顶部约束固定到 superview 顶部,常量 = 0(当导航栏半透明时,tableView 将位于导航栏和状态栏下方)

之后,如果您点击状态栏,tableview 会停在正确的位置。

我已经通过在滚动到顶部后手动将 contentOffset 设置为 0 来修复它。

func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
    return true
}

func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
    scrollView.contentOffset.y = 0.0
}

在您的 ViewController 中声明一个 Bool 类型的 var didBeginScrollToTop。 将控制器指定为 scrollView 的委托。然后

func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
    didBeginScrollToTop = true
    return true
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    guard didBeginScrollToTop, scrollToTopGestureTargetView?.contentOffset.y ?? 0 < -25 else { return }

    scrollToTopGestureTargetView?.setContentOffset(.zero, animated: true)
}

func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    didBeginScrollToTop = false
}

这样做是在弹跳时调整您的内容偏移量。该标志用于捕获 scrollToTop 手势并在滚动时重置。您也可以在设置 .zero 偏移量后将其关闭。如果您有子 VC 的实现,请不要忘记在重写这 3 个委托方法时调用 super。 现在我的第一个方法是跟踪内容 offSet 何时 < 0,但是在某些设备上它没有展开导航项并且魔法 -25 似乎适用于所有模拟器。您可以将此方法与 returns 导航栏大小的实现结合起来并替换它,但据我所知,没有一种简单的方法可以如此轻松地获得导航栏 frame/bounds .

注意你可能还需要处理这个:

我遇到了同样的问题。我在 UINavigationController 中嵌入了 UIViewController 中的集合视图。 Collectionview 对安全区域有前导、尾随、顶部、底部约束。

要解决此问题,您需要:

  1. 将 collectionview 的顶部约束更改为 supervises(非安全区域)
  2. 在 viewDidLoad 方法中设置 extendedLayoutIncludesOpaqueBars = true