After working with UIKit
for several years I am still quite new to SwiftUI and especially to its navigation features.
Currently I am trying to get my head around NavigationStack
and .navigationDestination
. It seems that that using these features allows push
navigation (like in UINavigationController
) only. Is this correct?
When searching for ways to use NavigationStack
with .sheet
and/or .fullScreenCover
navigation instead I find several examples which state that this is possible but has to be implemented manually:
struct ContentView: View {
@State private var navigationPath = NavigationPath()
@State private var presentingSheet: Bool = false
@State private var presentingFullScreen: Bool = false
@State private var selectedItem: String?
var body: some View {
NavigationStack(path: $navigationPath) {
VStack {
/*Button("Push to Detail") {
navigationPath.append("Detail")
}*/
Button("Present as Sheet") {
selectedItem = "Detail"
presentingSheet = true
}
Button("Present as FullScreen") {
selectedItem = "Detail"
presentingFullScreen = true
}
}
/*.navigationDestination(for: String.self) { value in
DetailView(value: value)
}*/
.sheet(isPresented: $presentingSheet) {
if let selectedItem {
DetailView(value: selectedItem)
}
}
.fullScreenCover(isPresented: $presentingFullScreen) {
if let selectedItem {
DetailView(value: selectedItem)
}
}
}
}
}
While this obviously works, I do not understand what advantage I get from using NavigationStack
in this example. NavigationPath
is only utilized when using the push navigation but not when using .sheet
or .fullStackCover
, is it?
So, is there any reason to use NavigationStack
at all, when push navigation is not used?
Is there any iOS/SwiftUI native solution when working with .sheet
or .fullStackCover
or to I need a custom or third party solution like the Routing Pattern?
After working with UIKit
for several years I am still quite new to SwiftUI and especially to its navigation features.
Currently I am trying to get my head around NavigationStack
and .navigationDestination
. It seems that that using these features allows push
navigation (like in UINavigationController
) only. Is this correct?
When searching for ways to use NavigationStack
with .sheet
and/or .fullScreenCover
navigation instead I find several examples which state that this is possible but has to be implemented manually:
struct ContentView: View {
@State private var navigationPath = NavigationPath()
@State private var presentingSheet: Bool = false
@State private var presentingFullScreen: Bool = false
@State private var selectedItem: String?
var body: some View {
NavigationStack(path: $navigationPath) {
VStack {
/*Button("Push to Detail") {
navigationPath.append("Detail")
}*/
Button("Present as Sheet") {
selectedItem = "Detail"
presentingSheet = true
}
Button("Present as FullScreen") {
selectedItem = "Detail"
presentingFullScreen = true
}
}
/*.navigationDestination(for: String.self) { value in
DetailView(value: value)
}*/
.sheet(isPresented: $presentingSheet) {
if let selectedItem {
DetailView(value: selectedItem)
}
}
.fullScreenCover(isPresented: $presentingFullScreen) {
if let selectedItem {
DetailView(value: selectedItem)
}
}
}
}
}
While this obviously works, I do not understand what advantage I get from using NavigationStack
in this example. NavigationPath
is only utilized when using the push navigation but not when using .sheet
or .fullStackCover
, is it?
So, is there any reason to use NavigationStack
at all, when push navigation is not used?
Is there any iOS/SwiftUI native solution when working with .sheet
or .fullStackCover
or to I need a custom or third party solution like the Routing Pattern?
2 Answers
Reset to default 0In SwiftUI NavigationStack
is unrelated to the sheet
and fullScreenCover
.
NavigationLink
is like show
with the controller.
If you want the global show
and push
or present
you do need a custom setup. SwiftUI does not have a global push
.
The presenting a screen functionality using sheet
/fullScreenCover
in SwiftUI has nothing to do with the navigation stack like in UIKit where in we require a navigation controller to either push or present the new screen.
SwiftUI Screens operates via views and not like controllers. In these cases the modifiers sheet
/fullScreenCover
are used to present the required view.
NavigationStack
is useful when we want to operate the push functionality essentially linking a SwiftUI view to a particular destination.
If you want a global functionality I would recommend creating a boiler BaseView with fullScreenCover
and have a Navigation functionality via there all handled by a ViewModel to take advantage of MVVM pattern.
BaseView That Wraps All Screens
struct BaseView<Content: View>: View { @StateObject private var viewModel = BaseViewModel() let content: Content // Child view to be wrapped var body: some View { content .fullScreenCover(item: $viewModel.activeFullScreenCover) { cover in viewModel.destinationView(for: cover) } .environmentObject(viewModel) } }
BaseViewModel for Global Navigation
class BaseViewModel: ObservableObject { @Published var activeFullScreenCover: AnyView? func presentFullScreenCover<Content: View>(_ view: Content) { activeFullScreenCover = AnyView(view) } func dismissFullScreenCover() { activeFullScreenCover = nil } }
Make Sure to wrap the views inside Base view
@main struct MyApp: App { var body: some Scene { WindowGroup { BaseView(content: HomeView()) // HomeView is now inside BaseView } } }
Child Views Can Trigger Full-Screen Presentation
struct HomeView: View { @EnvironmentObject var viewModel: BaseViewModel var body: some View { VStack { Button("Show Full-Screen Modal") { viewModel.presentFullScreenCover(ChildView()) } } .navigationTitle("Home") } }
sheet
andfullScreenCover
are modal presentations, so it's a bit different to navigation. See the Apple documentation for an overview of intended use. That page also has a link to design guidlines, for guidance on when this approach is appropriate. – Benzy Neez Commented 15 hours ago.sheet(item: selectedItem)
– malhal Commented 13 hours ago