How to Dynamically Hide Navigation Back Button in Swiftui

How to dynamically hide navigation back button in SwiftUI

Here is working solution. Back button cannot be hidden, it is managed by bar and owned by parent view, however it is possible to hide entire navigation bar with below approach.

Tested with Xcode 11.4 / iOS 13.4

demo

struct ParentView: View {
@State var isTimerRunning = false
var body: some View {
NavigationView {
VStack {
NavigationLink("Go", destination: TimerTest(isTimerRunning: $isTimerRunning))
}
.navigationBarHidden(isTimerRunning)
.navigationBarTitle("Main") // << required, at least empty !!
}
}
}

struct TimerTest: View {
@Binding var isTimerRunning: Bool

var body: some View {
Button(action:self.startTimer) {
Text("Start Timer")
}
}

func startTimer()
{
self.isTimerRunning = true

_ = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { timer in
DispatchQueue.main.async { // << required !!
self.isTimerRunning = false
}
}
}
}

Swift - How to hide back button in navigation item?

According to the documentation for UINavigationItem :

self.navigationItem.setHidesBackButton(true, animated: true)

How to hide NavigationView Bar in SwiftUI

Seems that the solution could be adding a title or removing the space from safe area.

The problem:

Sample Image

Solution 1:

.navigationBarHidden(true)
.navigationBarTitle(Text("Home"))

Solution 2 (this seems be the best):

.navigationBarHidden(true)
.navigationBarTitle(Text("Home"))
.edgesIgnoringSafeArea([.top, .bottom])

Sample Image

Is it possible to disable the back navigation menu in iOS 14+?

It can be done by subclassing UIBarButtonItem. Setting the menu to nil on a UIBarButtonItem doesn't work, but you can override the menu property and prevent setting it in the first place.

class BackBarButtonItem: UIBarButtonItem {
@available(iOS 14.0, *)
override var menu: UIMenu? {
set {
// Don't set the menu here
// super.menu = menu
}
get {
return super.menu
}
}
}

Then you can configure the back button in your view controller the way you like, but using BackBarButtonItem instead of UIBarButtonItem.

let backButton = BackBarButtonItem(title: "BACK", style: .plain, target: nil, action: nil)
navigationItem.backBarButtonItem = backButton

This is preferred because you set the backBarButtonItem only once in your view controller's navigation item, and then whatever view controller it will be pushing, the pushed controller will show the back button automatically on the nav bar. If using leftBarButtonItem instead of backBarButtonItem, you will have to set it on every view controller that will be pushed.

Edit:

The back navigation menu that appears on long press is a property of UIBarButtonItem. The back button of a view controller can be customized by setting the navigationItem.backBarButtonItem property and by doing so we can control the menu. The only problem with this approach that I see is losing the localization (translation) of the "Back" string which the system button has.

If you want the disabled menu to be the default behaviour you can implement this in one place, in a UINavigationController subclass conforming to UINavigationControllerDelegate:

class NavigationController: UINavigationController, UINavigationControllerDelegate {
  init() {
    super.init(rootViewController: ViewController())
    delegate = self
  }
   
  func navigationController(_ navigationController: UINavigationController,
                 willShow viewController: UIViewController, animated: Bool) {
    let backButton = BackBarButtonItem(title: "Back", style: .plain, target: nil, action: nil)
viewController.navigationItem.backBarButtonItem = backButton
  }
}

iOS SwiftUI: pop or dismiss view programmatically

This example uses the new environment var documented in the Beta 5 Release Notes, which was using a value property. It was changed in a later beta to use a wrappedValue property. This example is now current for the GM version. This exact same concept works to dismiss Modal views presented with the .sheet modifier.

import SwiftUI

struct DetailView: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var body: some View {
Button(
"Here is Detail View. Tap to go back.",
action: { self.presentationMode.wrappedValue.dismiss() }
)
}
}

struct RootView: View {
var body: some View {
VStack {
NavigationLink(destination: DetailView())
{ Text("I am Root. Tap for Detail View.") }
}
}
}

struct ContentView: View {
var body: some View {
NavigationView {
RootView()
}
}
}


Related Topics



Leave a reply



Submit