How to navigate between NavigationLink while leave a part of main window stay the same in SwiftUI

I would like to navigate between different NavigationLinks in NavigationView while some part of the main window stay the same. For example, I want to make a music app and I want to let the play controller always on top, while I can display different navigation contents (songs page, artists page...) using the rest of the window.

Like what's showed in the picture below, I want to keep the red part always there while the blue part changes.

Navigation Example Picture

My code would be like below, but it won't work correctly. The AlwaysStayView() disappears when I click any NavigationLink on sidebar. So, how can I correct it or is there any solution (prefer in SwiftUI, but framework like UIKit would also be OK). I would appreciate it.

NavigationView {
            List {
                NavigationLink { DiscoverView() }
                    label: { Label("Discover", systemImage: "magnifyingglass") }
                NavigationLink { SongsView() }
                    label: { Label("Songs", systemImage: "music.note") }
                NavigationLink { ArtistsView() }
                    label: { Label("Artists", systemImage: "music.mic") }
                }
            }
            .listStyle(SidebarListStyle())

            VStack {
                AlwaysStayView()
                SongsView()
            }
}

2 answers

  • answered 2022-05-07 05:42 Asperi

    In this case the default details view and navigated details view should be the same, but updated content can be injected in it in navigation link.

    Here is a demo. Tested with Xcode 13.3 / iPadOS 15.4

    demo

    struct ContentView: View {
        var body: some View {
            NavigationView {
                List {
                    NavigationLink { DetailsView { DiscoverView() } }
                      label: { Label("Discover", systemImage: "magnifyingglass") }
                    NavigationLink { DetailsView { SongsView() } }
                      label: { Label("Songs", systemImage: "music.note") }
                    NavigationLink { DetailsView { ArtistsView() } }
                      label: { Label("Artists", systemImage: "music.mic") }
                }
                .navigationBarHidden(true)
                .listStyle(SidebarListStyle())
    
                DetailsView { SongsView() }  // << here default !!
            }
        }
    }
    
    struct DetailsView<V: View>: View {
        @ViewBuilder var content: () -> V  // << injected via builder !!
        var body: some View {
            VStack(spacing: 0) {
                AlwaysStayView()
                content()          // << changed part here !!
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
            .navigationBarHidden(true)
        }
    }
    

    backup

  • answered 2022-05-07 08:27 ChrisR

    The NavigationLink from sidebar always exchanges the whole right screen area. So you would have to put your AlwaysStayView inside the navigation links – in each. Either on top level or inside the respective detail views. Here is one example:

    struct ContentView: View {
        var body: some View {
            NavigationView {
                List {
                    NavigationLink {
                        DetailView(title: "Always stay", color: .red).frame(height: 100)
                        DetailView(title: "Discover", color: .blue) }
                        label: { Label("Discover", systemImage: "magnifyingglass") }
                    
                    NavigationLink {
                        DetailView(title: "Always stay", color: .red).frame(height: 100)
                        DetailView(title: "Songs", color: .teal) }
                        label: { Label("Songs", systemImage: "music.note") }
                    
                    NavigationLink {
                        DetailView(title: "Always stay", color: .red).frame(height: 100)
                        DetailView(title: "Artists", color: .mint) }
                        label: { Label("Artists", systemImage: "music.mic") }
                }
                .listStyle(.sidebar)
                
                // Standard view if no item is lelected
                VStack {
                    DetailView(title: "Always stay", color: .red).frame(height: 100)
                    DetailView(title: "Songs", color: .teal)
                }
            }
            .toolbar {
                ToolbarItem(placement: .principal) {
                    Text("Toolbar")
                }
            }
        }
    }
    
    
    struct DetailView: View {
        
        let title: String
        let color: Color
        
        var body: some View {
            Text(title).font(.title)
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background(color)
        }
    }
    

How many English words
do you know?
Test your English vocabulary size, and measure
how many words do you know
Online Test
Powered by Examplum