【问题标题】:LargeTitles UIScrollView does not support multiple observers implementing _scrollViewWillEndDraggingWithVelocity:targetContentOffsetLargeTitles UIScrollView 不支持多个观察者实现 _scrollViewWillEndDraggingWithVelocity:targetContentOffset
【发布时间】:2018-05-31 23:18:00
【问题描述】:

我使用以下代码在我的应用中实现了大标题:

if #available(iOS 11.0, *) {
            navigationController?.navigationBar.prefersLargeTitles = true
            navigationItem.largeTitleDisplayMode = .always
        } else {
            // Fallback on earlier versions
        }
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if scrollView.contentOffset.y <= 0 {
        if #available(iOS 11.0, *) {
            self.navigationItem.largeTitleDisplayMode = .always
        } else {
            // Fallback on earlier versions
        }
    } else {
        if #available(iOS 11.0, *) {
            self.navigationItem.largeTitleDisplayMode = .never
        } else {
            // Fallback on earlier versions
        }
    }
    self.navigationController?.navigationBar.setNeedsLayout()
    self.view.setNeedsLayout()
    UIView.animate(withDuration: 0.01, animations: {
        self.navigationController?.navigationBar.layoutIfNeeded()
        self.view.layoutIfNeeded()
    })
}

我能够在标签栏上的视图之间成功切换,但是当我将视图推送到标签栏控制器的顶部,然后使用以下代码将其弹出时:

_ = self.navigationController?.popViewController(animated: true)

当我再次在标签栏上的视图之间切换时,我遇到了这个崩溃: 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“错误:UIScrollView 不支持多个观察者实现 _scrollViewWillEndDraggingWithVelocity:targetContentOffset:”

【问题讨论】:

  • 我遇到了同样的问题 :(
  • 当我转到另一个视图时,tableview 仍在滚动时出现问题。我通过在 scrollViewDidScroll 中设置一个 bool 来解决这个问题,它在 segue 启动时禁用任何滚动。

标签: swift uiscrollview crash navigationbar tabbar


【解决方案1】:

这不是解决方案,而是您需要在代码中调查的潜在问题。我收到了同样的错误消息(UIScrollView 不支持实现_scrollViewWillEndDraggingWithVelocity:targetContentOffset 的多个观察者),我注意到我做错了什么。 我在使用 NavigationView 的 SwiftUI 应用程序中收到此错误消息。

我犯的错误是 ParentView 在根目录下有一个 Navigation View。使用 NavigationLink 我正在移动到 ChildView,它也有一个 NavigationView 作为根。这是它在代码中的样子:

import SwiftUI

@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ParentView()
        }
    }
}

struct ParentView: View {
    var body: some View {
        NavigationView {
            List {
                NavigationLink(destination: ChildView()) {
                    Text("Parent view")
                }
            }
            .navigationTitle("Parent")
        }
    }
}

struct ChildView: View {
    var body: some View {
        List {
            ForEach(0 ..< 5) { _ in
                Text("Child view")
            }
        }
        .navigationTitle("Child")
    }
}

最初是 ChildView 的样子:

struct ChildView: View {
    var body: some View {
        NavigationView {
            List {
                ForEach(0 ..< 5) { _ in
                    Text("Second screen")
                }
            }
            .navigationTitle("Second")
        }
    }
}

请注意我是如何尝试推送一个本身嵌入在 NavigationView 中的视图。如第一个 sn-p 所示删除它,处理错误消息。您可以尝试调查一下,也许您只是在 UIKit 而不是 SwiftUI 中犯了同样的错误。

【讨论】:

  • 是的,我犯了这个错误,因为我是新手。
  • 这应该是公认的答案。尝试使用NavigationLink 导致另一个NavigationView 是错误使用NavigationViews。这与尝试将NavigationController 推送到UIKit 中的另一个NavigationController 几乎相同。当谈到 SwiftUI 时,Coordinator/Router 层是一个有趣的话题。也许不应该将 NavigationView 嵌入到场景视图的主体中,或者至少应该依赖于一些 @Environment 变量。
【解决方案2】:

当我转到另一个视图时,表格视图仍在滚动时发生了问题。我通过在 scrollViewDidScroll 中设置一个 bool 来解决这个问题,当 segue 启动时禁用任何滚动。

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if viewIsVisible {
            if scrollView.contentOffset.y <= 0 {
                if #available(iOS 11.0, *) {
                    self.navigationItem.largeTitleDisplayMode = .always
                } else {
                    // Fallback on earlier versions
                }
            } else {
                if #available(iOS 11.0, *) {
                    self.navigationItem.largeTitleDisplayMode = .never
                } else {
                    // Fallback on earlier versions
                }
            }
            self.navigationController?.navigationBar.setNeedsLayout()
            self.view.setNeedsLayout()
            UIView.animate(withDuration: 0.01, animations: {
                self.navigationController?.navigationBar.layoutIfNeeded()
                self.view.layoutIfNeeded()
            })
        }
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        self.viewIsVisible = false
}

【讨论】:

    【解决方案3】:

    我遇到了同样的问题,我通过从AppDelegate 中删除这一行来解决它:

    UINavigationBar.appearance().prefersLargeTitles = true
    

    并在某些UIViewController中处理prefersLargeTitles内的viewDidLoad

    【讨论】:

      【解决方案4】:

      我找到了解决方案。您必须将第一个导航控制器设置为不使用大标题。

      关键是现在 UIScrollView 只有一个观察者(navigationController)实现 _scrollViewWillEndDraggingWithVelocity。

      if (@available(iOS 11.0, *)) {
      
          self.navigationController.navigationBar.prefersLargeTitles = FALSE;
          self.navigationController.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeNever;
      
      }
      

      【讨论】:

        【解决方案5】:

        我认为以上所有答案都不能真正解决问题并且过于复杂。我建议在UIViewController 的每个子类中启用/禁用大标题,这样它们就不会同时使用大标题。这样做的好地方是在viewWillAppearviewWillDisappear 方法中

        override func viewDidLoad() {
            super.viewDidLoad()
            navigationItem.largeTitleDisplayMode = .always
        }
        
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            navigationController?.navigationBar.prefersLargeTitles = true
        }
        
        override func viewWillDisappear(_ animated: Bool) {
            super.viewWillDisappear(animated)
            navigationController?.navigationBar.prefersLargeTitles = false
        }
        

        【讨论】:

          猜你喜欢
          • 2014-09-08
          • 1970-01-01
          • 2013-12-03
          • 1970-01-01
          • 2021-04-13
          • 1970-01-01
          • 2012-05-11
          • 2022-11-17
          • 2019-10-09
          相关资源
          最近更新 更多