SwiftUI 中 NavigationSplitView 的编程式导航

问题描述 投票:0回答:2

我有一个最初使用 NavigationSplitView 为 iPad 设计的应用程序。在手机上,我希望它在某些情况下自动导航到详细视图。我找到了 NavigationLink 的 isActive 属性,但这仅适用于 NavigationStack,不适用于分割视图。有没有一种方法可以做到这一点,而无需将所有内容都转换为 NavigationStack,或者不必为手机和平板电脑构建单独的视图?

struct ScoringSplitView: View {
    @State var game: Game?
    @State var newGameViewModel: NewGameViewModel?

    var body: some View {
        NavigationSplitView {
            leftPanel
        } detail: {
            if let game = game {
                GameAlertContainerView(game: game)
            } else {
                GameScoringView(game: nil)
            }
        }
    }

    var leftPanel: some View {
        if let game = game {
            return AnyView(PlayerList(game: game))
        } else if let viewModel = newGameViewModel {
            return AnyView(NewGameView(viewModel: viewModel))
        } else {
            return AnyView(Text("placeholder")
        }
    }
struct PlayerList: View {
    @ObservedObject var game: Game

    var body: some View {
        VStack {
            TeamNamesView(game: game)
            BattingPlayerList(game: game)
        }
        .toolbar {
            if UIDevice.isIPhone { // from an extension
                ToolbarItem {
                    NavigationLink("Score") { // isActive doesn't work
                        GameAlertContainerView(game: game)
                    }
                }
            }

现在,“得分”按钮会将用户带到得分视图,但我希望它能够通过返回按钮自动转到玩家列表

ios swift ipad swiftui swiftui-navigationsplitview
2个回答
1
投票

我想出了如何使用 navigationDestination 视图修饰符和 isPresented 来做到这一点:

@State var showScore = true
// ...
        .navigationDestination(isPresented: $showScore) {
            GameAlertContainerView(game: game)
        }

0
投票

为了同时在 macOS 和 iOS 上工作,您需要使用两种不同的技术。您需要管理不同的导航方式:

  • 通过NavigationLinks进行正常导航
  • 编程式导航,在 macOS 和 iOS 中是分开的。

首先,创建一个 ViewModel(理论上你可以不做,但相信我)

@Observable
final class ViewModel {     
    /// used in Detail View of NavigationSplitView for macOS/iPad
    var selectedDetail: Detail? = nil
    /// used in Content View of NavigationSplitView for macOS
    var selectedContent: Content= = nil
    /// used to skip Content view on iOS
    var showDetail: Bool = false
    
    /// can be called from Toolbar
    func addDetail() {
        // create here
        let newDetail = ....
        #if os(macOS)
            select(content: selectedContent, detail: newDetail)
        #endif
        #if os(iOS)
            selectedDetail = newDetail
            showDetail = true
        #endif
    }

    // normal macOS navigation
    func select(content: Content?, detail: Detail?){
        selectedContent = content
        selectedDetail = detail
    }

    /// more stuff
}

在内容视图中:

  @State var model: ViewModel 

然后在视图正文中:

 NavigationSplitView {
        LeftSideView(model: model)
            .navigationDestination(isPresented: $model.showDetail){
                 if let detail = model.selectedDetail {
                     DetailView(detail: detail)
                 }
             }
        } content: {
             ContentView(model: model) //uses model.selectedContent
        } detail: {
            if let detail = model.selectedDetail {
                DetailView(model: model, detail: detail)
            } else {
               Text("Select a detail")
            }
        }

如您所见,有两种显示 DetailView 的方法,一种是通过 .navigationDestination,另一种是在详细视图中。

最后,您需要管理标准导航:

struct LinkToDetail: View {
    @Bindable var model: ViewModel
    @Bindable var detail: Detail
    let content: Detail

    var body: some View {
        #if os(macOS)
            Text(detail.title) 
                .onTapGesture {model.select(which: list, item: item)}
        #endif
        #if os(iOS)
            NavigationLink {
                DetailView(model: model, item: item)
            } label: {
                Text(detail.title) 
            }
         #endif
     }
}

如果您认为这太复杂了,那么您并不孤单。但至少它有效。

我很高兴听到它是如何工作的。 :-)

© www.soinside.com 2019 - 2024. All rights reserved.