I have implemented large titles in my app with the following code:
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()
})
}
I am able to successfully toggle between views on a tabbar but when I push a view ontop of the tabbar controller and then pop it off using this code:
_ = self.navigationController?.popViewController(animated: true)
I get this crash when I toggle between views on the tabbar again: Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'ERROR: UIScrollView does not support multiple observers implementing _scrollViewWillEndDraggingWithVelocity:targetContentOffset:'
This is not a solution, but a potential thing that you need to investigate in your code. I got this same error message (UIScrollView does not support multiple observers implementing _scrollViewWillEndDraggingWithVelocity:targetContentOffset
) and I noticed I was doing something incorrectly.
I got this error message in a SwiftUI app using NavigationView.
The mistake I had made was that ParentView had a Navigation View at the root. Using a NavigationLink I was moving to ChildView, which also had a NavigationView as the root. Here's what it looked like in code:
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")
}
}
Initially this is what ChildView
looked like:
struct ChildView: View {
var body: some View {
NavigationView {
List {
ForEach(0 ..< 5) { _ in
Text("Second screen")
}
}
.navigationTitle("Second")
}
}
}
Notice how I was trying to push a view which itself was embedded in a NavigationView. Removing it as shown in the first snippet, took care of the error messages. You can try looking into that, maybe you are doing the same mistake just in UIKit instead of SwiftUI.
The problem happened when the tableview was still scrolling when I went to another view. I fixed the problem by setting a bool in the scrollViewDidScroll that disables any scrolling when the segue is started.
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
}
I found the solution. You have to set the first navigation controller to not use large titles.
The point is that now UIScrollView has only one observer (navigationController) implementing _scrollViewWillEndDraggingWithVelocity.
if (@available(iOS 11.0, *)) {
self.navigationController.navigationBar.prefersLargeTitles = FALSE;
self.navigationController.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeNever;
}
I've the same problem and I fixed it by removing this line from AppDelegate
:
UINavigationBar.appearance().prefersLargeTitles = true
and handle prefersLargeTitles
inside viewDidLoad
in certain UIViewController
I think all of above answers don't really solve the issue and are overcomplicated. I recommend enabling/disabling large titles in each of your UIViewController
's subclasses, so they don't use large titles at the same time. Good place to do it is in the viewWillAppear
and viewWillDisappear
methods
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With