skip to Main Content

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 ?

2

Answers


  1. Chosen as BEST ANSWER

    I asked this in the R/SwiftUI too and I received a response that may help who is having the same problem.

    The response:

    https://www.reddit.com/r/SwiftUI/comments/1b8284a/comment/ktmuxoe/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button

    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.


  2. I don’t have a direct answer to your question but I can offer you a different approach. Maybe this will suit you?

    @State var navToLogin: Bool = false
    @EnvironmentObject var navigationViewModel: NavigatorViewModel
    
    var body: some View  {
        if navToLogin {
            LoginViewScreen()
        } else {
            NavigationView {
                VStack {
                    ExempleView()
                        .onAppear() {
                            Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { timer in
                                navToLogin = true
                            }
                        }
                    Button("Go to Login Page") {
                        navToLogin.toggle()
                    }
                }
            }
        }
    }    
    

    We use a timer instead of sleep to avoid blocking the thread and the display is only conditioned on the value of navToLogin.

    Happy coding!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search