我有一个最初使用 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)
}
}
}
现在,“得分”按钮会将用户带到得分视图,但我希望它能够通过返回按钮自动转到玩家列表
我想出了如何使用 navigationDestination 视图修饰符和 isPresented 来做到这一点:
@State var showScore = true
// ...
.navigationDestination(isPresented: $showScore) {
GameAlertContainerView(game: game)
}
为了同时在 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
}
}
如果您认为这太复杂了,那么您并不孤单。但至少它有效。
我很高兴听到它是如何工作的。 :-)