NEW: My new book Pro SwiftUI is out now – level up your SwiftUI skills today! >>

NavigationLinks Broken

Forums > SwiftUI

I have an app that Im trying to write a custom launch screen for. The AppTabView gets called in the view that conforms to App. As you can see if showTabView is false it shows the LaunchScreen and that view after a few seconds will toggle showTabView and then show the TabView. All that works fine. The problem is that in each of the views surrounded by NavigationViews all the NavigationLinks dont animate to the view (slide in). If I remove all the code and just show TabView then everything works fine.

I cannot seem to figure out why. I have tried adding tags to the NavigationLinks and that doesnt seem to work. Can anyone tell me why showing a launch screen this way would break all the navigation links.

struct AppTabView: View {
    @AppStorage(StorageKeys.savedVersion.rawValue) var savedVersion = ""

    @State private var showWhatsNewView = false
    @State private var currentDate = Date()
    @State private var showTabView = false

    var body: some View {
        Group {
            if !showTabView {
                LaunchScreen(showTabView: $showTabView)
            } else {
                TabView {
                    NavigationView { EventsView() }
                        .tabItem {
                            Label(String(localized: "Events", table: "AppTabView+Strings", comment: "The name of the house tab"), systemImage: "house")
                        }
                    NavigationView { ContactsView() }
                        .tabItem {
                            Label(String(localized: "Contacts", table: "AppTabView+Strings", comment: "The name of the house tab"), systemImage: "person.2.crop.square.stack.fill")
                        }
                    NavigationView { CustomDatePickerView(currentDate: $currentDate) }
                        .tabItem {
                            Label(String(localized: "Calendar", table: "AppTabView+Strings", comment: "The name of the house tab"), systemImage: "calendar")
                        }
                }
                .navigationViewStyle(.stack)
                .accentColor(Color.themeBlue)
                .onAppear(perform: {
                    // MDK01-15-22 -- This fixes the issue with the tab view going transparent
                    let appearance = UITabBarAppearance()
                    appearance.backgroundEffect = UIBlurEffect(style: .systemUltraThinMaterial)

                    // Use this appearance when scrolling behind the TabView
                    UITabBar.appearance().standardAppearance = appearance

                    // Use this appearance when scrolled all the way up
                    UITabBar.appearance().scrollEdgeAppearance = appearance

                    // Correct issue with navigation bar as well
                    let navBarAppearance = UINavigationBarAppearance()
                    navBarAppearance.backgroundEffect = UIBlurEffect(style: .systemUltraThinMaterial)
                    navBarAppearance.backgroundColor = UIColor(Color.themeBlue.opacity(Constants.pt2))
                    // Leave this in, just in case I decide to change the font color of the nav bar
                    // Use this if NavigationBarTitle is with Large Font
                    navBarAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.label]

                    // Use this if NavigationBarTitle is with displayMode = .inline
                    navBarAppearance.titleTextAttributes = [.foregroundColor: UIColor.label]

                    // Use this appearance when scrolling behind the NavBar
                    UINavigationBar.appearance().standardAppearance = navBarAppearance
                    // Use this appearance when scrolled all the way down
                    UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearance

                    // MDK01-15-22 -- determine if we need to show the what's new view
                    DispatchQueue.main.asyncAfter(deadline: .now() + Constants.pt5, execute: {
                        withAnimation {
                            showWhatsNewView = checkForNewVersion()
                        }
                    })

                    // MDK-03-20-22 -- Added support for requesting app reviews
                    AppReviewRequest.requestReviewIfAppropriate()
                })
                .sheet(isPresented: $showWhatsNewView) {
                    WelcomeView()
                        .interactiveDismissDisabled()
                }
            }
        }
    }

    /// Checks to see if a new version of the app has been loaded
    ///
    /// This function helps me determine if the app has been updated and
    /// if it has then show the WelcomeScreen again to info the user of any
    /// new features
    ///
    /// - Returns: True if app has been updated otherwise false
    private func checkForNewVersion() -> Bool {
        var showWhatsNew = false

        // Retrieve the current version, Ex: Version 1.0.0 build 15
        let currentVersion = DeviceInfo.getVersionString()
        let mySavedVersion = savedVersion

        if mySavedVersion != currentVersion {
            showWhatsNew = true
            savedVersion = currentVersion
        }

        return showWhatsNew
    }

}

   

I tried reproducing your problem (with Xcode 13.4.1) on simulator and devices (iOS 15.5) and my NavigationLinks (leading from the primary view to a secondary) are all properly anymating in and out.

What is your test setup? Are you perhaps expecting something different? For example that switching the tabs is animated?

See my reproduced code im my Gist with a video showing proper animations https://gist.github.com/pd95/d78ecdb8b64342a29bb325af0b54c222

   

@pd95

I looked at your gist and the video. I do notice that your NavigationLinks are inside a Form. When you get into my code the NavigationLinks are not inside forms. Its weird though, that if I remove all the LaunchScreen stuff, the if statements and only have the TabView with its modifiers then all the NavigationLinks inside the other views, whether that view is in a VStack, HStack or ZStack all seem to work just fine. The view its navigating to slides in nice and easy.

I will go and try putting the views that have Nav Links in them inside a Form. Question, do navigation links have to be inside a form? I didn't think so. But I could be wrong.

Thanks Taz

   

@pd95

Unfortunately, I cannot put all my Nav Links inside Forms. The Links are inside a List and you cannot show a list in a form. So, Im stuck here. I do have a settings page thats a form and my nav link works there just fine. All the others that are inside List do not work. You tap them and they just pop the view on instead of slide it in.

   

No, the NavigationLinks work the same way when used in VStack instead of Form. I just tested it and on my phone they animate nicely.

Can you perhaps show us the code of your EventsView?

   

EventsView:

struct EventsView: View {
    @EnvironmentObject var dataManager: DataManager

    // This is the @FetchRequest that ties the view to CoreData Events
    @FetchRequest(fetchRequest: Event.allEventsFR())
    private var events: FetchedResults<Event>

    @State private var multiSectionDisplay = false

    var body: some View {
        VStack {
            if events.count == 0 {
                EmptySnapView(
                    viewTitle: String(localized: "No Events", table: "EmptyEventsView+Strings", comment: "Title of View"),
                    viewSubTitle: String(localized: "There are no events to show", table: "EmptyEventsView+Strings", comment: "Subtitle of View"),
                    viewInstruction: "To add events you must first import or manually enter your contacts. To do this tap the contacts tab in the middle and tap the plus button in the upper right. Either manually enter contacts, select contacts or add all contacts."
                )
            } else {
                EventsListView(multipleSectionDisplay: $multiSectionDisplay)
            }
        }
        .navigationTitle(.nbEventsViewTitle)
        .navigationBarTitleDisplayMode(.automatic)
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                SNAPSettingsView()
            }
        }
        .onDisappear {
            dataManager.save()
        }
    }
}

The EventsView calls the view EventsListView which is where the Nav Link is

struct EventsListView: View {
    @EnvironmentObject var dataManager: DataManager
    @AppStorage(StorageKeys.lastFirstFirstLast.rawValue) var lastFirstFirstLast = NameSort.firstLast

    @FetchRequest(fetchRequest: Event.allEventsFR())
    private var events: FetchedResults<Event>

    // Whether we are mulit-section or not
    @Binding var multipleSectionDisplay: Bool

    // Search Support
    @State private var searchTerm = ""

    // Filtering and sectioning the events for searching
    var sectionArray: [EventSectionData] {
        // Filter the list first
        let filteredList = events.filter { searchTerm.appearsIn(lastFirstFirstLast == .firstLast ? $0.cdContact!.fullNameFirstLast : $0.cdContact!.fullNameLastFirst) || searchTerm.appearsIn($0.eventName) }

        if !multipleSectionDisplay {
            let sortedEvents = filteredList.sorted(by: { $0.eventDate < $1.eventDate })

            return [EventSectionData(
                index: Constants.i1,
                title: String(localized: "Total Events: \(events.count)", table: "EventsListView+Strings", comment: "Tells us the total event count"),
                events: sortedEvents)]
        }

        // If we are doing a multiple sectioned array
        var sectionedEvents = [EventSectionData]()
        let dictionaryByContact = Dictionary(grouping: filteredList, by: { $0.cdContact })
        var index = 1

        if lastFirstFirstLast == .firstLast {
            for key in dictionaryByContact.keys.sorted(by: { ($0?.fullNameFirstLast)! < ($1?.fullNameFirstLast)! }) {
                sectionedEvents.append(EventSectionData(
                    index: index,
                    title: key?.fullNameFirstLast ?? "No Name",
                    events: dictionaryByContact[key]!
                ))
                index += 1
            }
        } else if lastFirstFirstLast == .lastFirst {
            for key in dictionaryByContact.keys.sorted(by: { ($0?.fullNameLastFirst)! < ($1?.fullNameLastFirst)! }) {
                sectionedEvents.append(EventSectionData(
                    index: index,
                    title: key?.fullNameLastFirst ?? "No Name",
                    events: dictionaryByContact[key]!
                ))
                index += 1
            }
        }
        return sectionedEvents
    }

    var body: some View {
        List {
            ForEach(sectionArray) { section in
                Section(header: SectionHeaderView(btnImg1: SFImage.person.image, btnImg2: SFImage.person2.image, secTitle: section.title, secIndex: section.index, multipleSectionDisplay: $multipleSectionDisplay)) {
                    ForEach(section.events) { event in
                        NavigationLink(destination: DisplayEventInfoView(event: event)) {
                            EventListRowView(event: event, multipleSectionDisplay: $multipleSectionDisplay)
                        }
                    }
                    .onDelete(perform: { offsets in deleteEvent(at: offsets, in: section.events) })
                }
            }
        }
        .listStyle(.insetGrouped)
        .searchable(text: $searchTerm, placement: .automatic, prompt: Text(.sbEventsListTitle))
    }

    private func deleteEvent(at offsets: IndexSet, in events: [Event]) {
        let eventsToDelete = offsets.map({ events[$0] })
        for event in eventsToDelete {
            withAnimation {
                dataManager.delete(event)
            }
        }
    }
}

   

Has anyone any idea why my Nav Links are not transitioning? I'm completely stuck on this one. Doesn't make sense. When you tap one the view just pops in instead of sliding in.

Earlier I posted my main view and then the view that has the link in it.

Any thoughts would be appreciated.

Taz

   

Hacking with Swift is sponsored by Play

SPONSORED Play is the first native iOS design tool created for designers and engineers. You can install Play for iOS and iPad today and sign up to check out the Beta of our macOS app with SwiftUI code export. We're also hiring engineers!

Click to learn more about Play!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.