如何在 NavigationLink 之间导航,同时保留一部分 main window 在 SwiftUI 中保持不变

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

我想在 NavigationView 中的不同 NavigationLink 之间导航,而主要 window 的某些部分保持不变。例如,我想制作一个音乐应用程序,我想让播放控制器始终在顶部,同时我可以使用 window 的其余部分显示不同的导航内容(歌曲页面、艺术家页面...)。

如下图所示,我希望红色部分一直存在,蓝色部分变化。

Navigation Example Picture

我的代码如下所示,但无法正常工作。单击边栏上的任何 NavigationLinkAlwaysStayView() 消失。那么,我该如何更正它或者是否有任何解决方案(更喜欢 SwiftUI,但像 UIKit 这样的框架也可以)。我将不胜感激。

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()
            }
}

在这种情况下,默认 详细信息视图和导航详细信息视图应该相同,但可以在导航中将更新的内容注入其中 link。

这是一个演示。使用 Xcode 13.3 / iPadOS 15.4

测试

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

侧边栏的 NavigationLink 始终交换整个右侧屏幕区域。所以你必须把你的 AlwaysStayView 放在导航链接里——在每一个里。在顶层或在各自的详细视图中。这是一个例子:

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)
    }
}