In some points of the code, I need to use this:
NavigationLink(
destination: LoginViewScreen(),
isActive: $navToLogin
) { EmptyView() }
... because my navigation is not using a button, I use only the property as trigger to navigate.
I'm receiving this warning:
use NavigationLink(value:label:) inside a List within a NavigationStack or NavigationSplitView
The problem is that minimum iOS target is 14.0 and I'm not able to use NavigationStack
because it is for iOS 16.0.
I would like something like that, where I can use the both ways. But I am not being able, because the property NavigationPath
needs to be a @State
, and the states need to be a property of the class, and if the property is iOS > 16, all the class is > 16 and a cannot instanciate it in my application
@State var navToLogin: Bool = false
@EnvironmentObject var navigationViewModel: NavigatorViewModel
var body: some View {
NavigationView {
VStack {
ExampleView()
.onAppear() {
Task {
try? await Task.sleep(nanoseconds: 5000000000)
navigateToLogin()
}
}
navigationToLogin
}
}
}
var navigationToLogin: some View {
NavigationLink(
destination: LoginViewScreen(),
isActive: $navToLogin
) { EmptyView() }
}
private func navigateToLogin() {
if #available(iOS 16, *) {
navigationViewModel.navigateTo(.loginScreen)
} else {
navToLogin.toggle()
}
}
Does anyone know how to work well with it ?
Or is there any other way ?
I asked this in the R/SwiftUI too and I received a response that may help who is having the same problem.
The response:
The solution:
public struct NavigationViewStack<V>: View where V: View {
@ViewBuilder private let content: () -> V
public init(content: @escaping () -> V) {
self.content = content
}
public var body: some View {
if #available(iOS 16, *) {
NavigationStack { content() }
} else {
NavigationView { content() }
}
}
}
public extension View {
@ViewBuilder
func navigationDestinationWrapper<V>(isPresented: Binding<Bool>, @ViewBuilder destination: () -> V) -> some View where V: View {
if #available(iOS 16, *) {
self.navigationDestination(isPresented: isPresented, destination: destination)
} else {
ZStack {
NavigationLink(isActive: isPresented, destination: destination, label: {
EmptyView()
})
self
}
}
}
@ViewBuilder
func navigationDestinationWrapper<D, C>(item: Binding<D?>, @ViewBuilder destination: @escaping (D) -> C) -> some View where D: Hashable, C: View {
if #available(iOS 17, *) {
self.navigationDestination(item: item, destination: destination)
} else {
ZStack {
NavigationLink(
destination: generateDestination(item, destination),
isActive: Binding<Bool>(
get: { item.wrappedValue != nil },
set: { _ in
item.wrappedValue = nil
}
),
label: { EmptyView() }
)
self
}
}
}
@ViewBuilder
private func generateDestination<D, C>(_ item: Binding<D?>, @ViewBuilder _ destination: @escaping (D) -> C) -> some View where D: Hashable, C: View {
if let unwrappedItem = item.wrappedValue {
destination(unwrappedItem)
} else {
EmptyView()
}
}
}
Example:
NavigationViewStack {
Text("First Page")
.navigationDestinationWrapper(isPresented: $presentSecondPage, destination: {
Text("Second Page")
})
}
Here, we create the NavigationViewStack to wrap NavigationStack and NavigationView.
The extensions will wrap the functions of NavigationStack to have a similar functionally in NavigationView.
The best solution is upgrade to the minimum iOS version to 16.0, but it will be useful to not broke when the NavigationView be deprecated.