我有一个名为 Handball Coach (NavigationSplitView) 的 iPad 应用程序,其详细信息部分中有一个 NavigationStack。 目前仅 TeamView 可用。
导航是选择一支球队并查看所有球员,或者您可以选择显示所有球员而不选择球队的选项。
当我通过单击 TeamItemView 从 TeamView 打开 PlayerView 时,然后单击玩家以显示 PlayerDetailView。一切正常。
但是当我使用 ToolBarItem 按钮打开 PlayerView 而不选择球队时,PlayerView 就会出现。当我单击播放器时,PlayerDetailView 没有出现。我仍然在 PlayerView 中。 当我单击后退按钮时,我位于 PlayerDetailView 中。当我单击后退按钮时,我就进入了 TeamView。
我希望您能理解我的问题。为什么PlayerDetailView没有出现而我仍然在PlayerView中?
这是我满怀希望的代码,你们中的任何人都看到我的错误或缺乏知识。
提前非常感谢。
这个效果很好。 ContentView -> TeamView -> 选择球队 -> PlayerView -> 选择球员 -> PlayerDetailView
这不起作用或行为错误。 ContentView -> TeamView -> ToolBarItem Button -> PlayerView -> 选择一个玩家 -> PlayerView 移动但停留在 PlayerView 中
struct ContentViewiPad: View {
@State var visibility: NavigationSplitViewVisibility = .all
@State var selectedSideMenuItem : SideMenuItems? = .team
@State private var navPath = NavigationPath()
let columns = [GridItem(.adaptive(minimum: 360))]
var body: some View {
NavigationSplitView(columnVisibility: $visibility) {
List(SideMenuItems.allCases, id: \.title, selection: $selectedSideMenuItem) { sideMenuItem in
NavigationLink(value: sideMenuItem) {
HStack {
sideMenuItem.image
.frame(width: 40)
Text(sideMenuItem.title)
}
.padding(.top, 10)
.padding(.bottom, 10)
}
}
} detail: {
NavigationStack(path: $navPath) {
switch selectedSideMenuItem {
case .team:
TeamViewiPad()
case .training:
EmptyView()
case .games:
EmptyView()
case .tools:
EmptyView()
case .settings:
EmptyView()
case .none:
EmptyView()
}
}
}
.navigationSplitViewStyle(.balanced)
.accentColor(.black)
}
}
struct TeamViewiPad: View {
@State private var viewState = 0 // 0 = loading data, 1 = loading is done
@State private var teams = [TeamData]()
@State private var showTeamItemActionButton = false
@State private var showPlayerView = false
@State private var showAlert = false
@State private var alertTitle = ""
@State private var alertMessage = ""
let columns = [GridItem(.adaptive(minimum: 280))]
let columnVSpace: CGFloat = 40 // vertical space between 2 player
var body: some View {
ScrollView {
if (self.viewState == 0) {
VStack() {
LoadingView(loadingText: "Daten werden geladen...")
}
.padding(.top, UIScreen.screenHeight / 4)
}
else {
LazyVGrid(columns: columns, spacing: columnVSpace) {
ForEach(teams, id: \.id) {team in
NavigationLink(value: team) {
TeamItemViewiPad(team: team, teams: $teams, showTeamItemActionButton: $showTeamItemActionButton)
}
}
}
.padding(.top, 20)
.padding(.bottom, 20)
}
}
.alert(alertTitle, isPresented: $showAlert, actions: {}, message: { Text(alertMessage) })
.navigationDestination(for: TeamData.self) { team in
PlayerViewiPad(team: team)
}
.navigationDestination(isPresented: $showPlayerView) { // for ToolbarItem Button
PlayerViewiPad(team: TeamEmptyData)
}
.navigationTitle("Mannschaft")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button(action: {
showPlayerView = true
}) {
Text("Alle Spieler anzeigen")
Image(systemName: "person.3.fill")
}
.padding(.trailing, 20)
}
}
.onAppear {
viewState = 0
Task {
teams = SQLiteManager.shared.loadTeams()
viewState = 1
}
}
}
}
struct PlayerViewiPad: View {
let team: TeamData
@State private var players = [PlayerData]()
@State private var showPlayerNewOrEditView = false
@State private var playerEmptyData = PlayerEmptyData
@State private var showAlert = false
@State private var alertTitle = ""
@State private var alertMessage = ""
let columns = [GridItem(.adaptive(minimum: 360))]
let columnVSpace: CGFloat = 50 // vertical space between 2 player
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: columnVSpace) {
ForEach(players, id: \.id) {player in
NavigationLink(value: player) {
PlayerItemViewiPad(player: player)
}
}
}
.padding(.top, 20)
.padding(.bottom, 20)
}
.alert(alertTitle, isPresented: $showAlert, actions: {}, message: { Text(alertMessage) })
.fullScreenCover(isPresented: $showPlayerNewOrEditView, content: {
PlayerNewOrEditViewiPad(team: team, player: $playerEmptyData, players: $players, playerOrderByType: $playerOrderByType)
.frame(maxWidth: 720, maxHeight: 720)
.clipShape(RoundedRectangle(cornerRadius: 10))
.shadow(color: Color(white: 0.7), radius: 20)
.presentationBackground {
Rectangle()
.fill(.ultraThinMaterial)
.background(.black.opacity(0.2))
}
})
.navigationDestination(for: PlayerData.self) { player in
PlayerDetailViewiPad(playerId: player.id, team: team, players: $players, playerOrderByType: $playerOrderByType)
}
.navigationTitle(team.teamName != "" ? team.teamName : "Alle Spieler")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
showPlayerNewOrEditView = true
} label: {
Image(systemName: "person.crop.circle.badge.plus")
}
.padding(.trailing, 20)
}
}
.onAppear {
Task {
players = SQLiteManager.shared.loadPlayersOfTeam(team: team, orderBy: playerOrderByType)
}
}
}
}
struct PlayerItemViewiPad: View {
let player: PlayerData
var body: some View {
HStack (alignment: .top) {
ZStack {
Image(uiImage: player.playerImage.imagePlayerFromBase64)
.resizable()
.frame(width: 130, height: 130)
Rectangle()
.fill(colorDarkGray)
.frame(width: 25, height: 25)
.offset(x: -52, y: -52)
Text("\(player.playerShirtNumber)")
.foregroundColor(.white)
.bold()
.font(.caption)
.offset(x: -52, y: -52)
}
.padding(.leading, 20)
.padding(.trailing, 10)
VStack (alignment: .leading) {
HStack {
Text(player.playerName)
.font(.system(size: 20, weight: .bold))
.foregroundColor(.black)
.lineLimit(1)
if player.playerNeedsAttention {
Image(systemName: "bookmark.fill")
.foregroundColor(colorBookmarkRed)
}
if player.playerHasBirthday {
Text("🎉")
}
}
.padding(.bottom, 10)
HStack (alignment: .center) {
Text("Werfen")
.font(.system(size: 15))
Image(PlayerSkillImage[player.playerSkillThrowBall]!)
.resizable()
.frame(width: 20, height: 20)
.padding(.trailing, 5)
Text("Fangen")
.font(.system(size: 15))
Image(PlayerSkillImage[player.playerSkillCatchBall]!)
.resizable()
.frame(width: 20, height: 20)
.padding(.trailing, 6)
Text("Passen")
.font(.system(size: 15))
.padding(.trailing, 2)
Image(PlayerSkillImage[player.playerSkillPassBall]!)
.resizable()
.frame(width: 20, height: 20)
Spacer()
}
.padding(.bottom, 10)
HStack (alignment: .center) {
Text("Prellen")
.font(.system(size: 15))
.padding(.trailing, 3)
Image(PlayerSkillImage[player.playerSkillBounceBall]!)
.resizable()
.frame(width: 20, height: 20)
.padding(.trailing, 4)
if player.playerType == 0 { // 0 = Field, 1 = Goalkeeper
Text("Spielen")
.font(.system(size: 15))
Image(PlayerSkillImage[player.playerSkillGame]!)
.resizable()
.frame(width: 20, height: 20)
.padding(.trailing, 4)
}
else {
Text("Torwart")
.font(.system(size: 15))
Image(PlayerSkillImage[player.playerSkillGoalkeeper]!)
.resizable()
.frame(width: 20, height: 20)
.padding(.trailing, 4)
}
Text("Athletik")
.font(.system(size: 15))
Image(PlayerSkillImage[player.playerSkillAthletic]!)
.resizable()
.frame(width: 20, height: 20)
Spacer()
}
.padding(.bottom, 10)
HStack (alignment: .center) {
Text(player.playerBirthdayTextLine)
.font(.system(size: 15))
.scaledToFill()
.minimumScaleFactor(0.5)
.lineLimit(1)
Spacer()
}
}
}
}
}
struct PlayerDetailViewiPad: View {
let playerId : Int
let team: TeamData // required for refreshing players
@Environment(\.presentationMode) var presentationMode
@Binding var players: [PlayerData] // player from PlayerViewiPad to refresh data after e.g. deletion
@Binding var playerOrderByType: PlayerOrderByType
@State private var player = PlayerEmptyData
@State private var showPlayerNewOrEditView = false
@State private var showDeleteConfirmationDialog = false
@State private var selectedDate: Date = Date()
@State private var showAlert = false
@State private var alertTitle = ""
@State private var alertMessage = ""
let rowHSpace: CGFloat = 35 // horizontal space
let rows = [GridItem(.adaptive(minimum: 150))]
var body: some View {
ScrollView {
VStack(alignment: .center) {
HStack(alignment: .top) {
Spacer()
ZStack {
Image(uiImage: player.playerImage.imagePlayerFromBase64)
.resizable()
.frame(width: 150, height: 150)
Rectangle()
.fill(colorDarkGray)
.frame(width: 30, height: 30)
.offset(x: -60, y: -60)
Text("\(player.playerShirtNumber)")
.foregroundColor(.white)
.bold()
.font(.system(size: 18, weight: .bold))
.offset(x: -60, y: -60)
}
.padding(.trailing, 20)
VStack(alignment: .leading) {
HStack (alignment: .center) {
Text(player.playerName)
.font(.system(size: 35, weight: .bold))
.foregroundColor(.black)
.lineLimit(1)
.padding(.bottom, 20)
if player.playerNeedsAttention {
Image(systemName: "bookmark.fill")
.font(.system(size: 25))
.foregroundColor(colorBookmarkRed)
.padding(.leading, 10)
.offset(x: 0, y: -8)
}
}
HStack {
Image(systemName: "calendar.and.person")
.padding(.trailing, 10)
Text("Jahrgang " + String(player.playerBirthdayYear))
.padding(.trailing, 45)
Image(systemName: "figure.handball")
.padding(.trailing, 5)
Text(player.playerThrowingArm)
}
.padding(.bottom, 20)
if player.playerAddress != "" {
HStack {
Image(systemName: "mappin.and.ellipse")
.padding(.trailing, 10)
Text(player.playerAddress)
}
}
}
.padding(.trailing, 100)
ZStack {
RoundedRectangle(cornerRadius: 5)
.fill(.white)
.frame(width: 130, height: 150)
.shadow(radius: 5)
VStack {
Text("\(player.playerBirthday.formatDateToString(formatString: "dd"))")
.font(.system(size: 50, weight: .bold))
.foregroundColor(colorDarkGray)
Text("\(player.playerBirthday.formatDateToString(formatString: "MMMM"))")
.font(.system(size: 20, weight: .bold))
.foregroundColor(colorDarkGray)
Divider()
.background(.gray)
.frame(width: 110)
.padding(.vertical, 5)
Text("Alter \(player.playerAge) (" + String(player.playerBirthday.formatDateToString(formatString: "EE)")))
.font(.system(size: 17))
.foregroundColor(colorDarkGray)
}
if player.playerHasBirthday {
Text("🎉")
.font(.system(size: 25))
.offset(x: 47, y: -57)
}
}
Spacer()
}
.padding(.bottom, 50)
HStack(alignment: .top) {
LazyHGrid(rows: rows, alignment: .center, spacing: rowHSpace) {
PlayerDetailSkillItemViewiPad(skillValue: player.playerSkillThrowBall, skillName: "Werfen")
PlayerDetailSkillItemViewiPad(skillValue: player.playerSkillCatchBall, skillName: "Fangen")
PlayerDetailSkillItemViewiPad(skillValue: player.playerSkillPassBall, skillName: "Passen")
PlayerDetailSkillItemViewiPad(skillValue: player.playerSkillBounceBall, skillName: "Prellen")
PlayerDetailSkillItemViewiPad(skillValue: player.playerSkillGame, skillName: "Spielen")
PlayerDetailSkillItemViewiPad(skillValue: player.playerSkillGoalkeeper, skillName: "Torwart")
PlayerDetailSkillItemViewiPad(skillValue: player.playerSkillAthletic, skillName: "Athletik")
}
}
HStack(alignment: .top) {
ZStack {
RoundedRectangle(cornerRadius: 5)
.fill(.white)
.frame(width: 80, height: 80)
.shadow(radius: 5)
VStack(alignment: .center) {
Text(String(player.playerSkillRating))
.font(.system(size: 35, weight: .bold))
.foregroundColor(colorDarkGreen)
.padding(.bottom, 3)
Text("Rating")
.font(.system(size: 14, weight: .bold))
}
}
}
}
.padding(.top, 40)
}
.alert(self.alertTitle, isPresented: self.$showAlert, actions: {}, message: { Text(self.alertMessage) })
.fullScreenCover(isPresented: $showPlayerNewOrEditView, content: {
PlayerNewOrEditViewiPad(team: team, player: $player, players: $players, playerOrderByType: $playerOrderByType)
.frame(maxWidth: 720, maxHeight: 720)
.clipShape(RoundedRectangle(cornerRadius: 10))
.shadow(color: Color(white: 0.7), radius: 20)
.presentationBackground {
Rectangle()
.fill(.ultraThinMaterial)
.background(.black.opacity(0.2))
}
})
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
showPlayerNewOrEditView = true
} label: {
Image(systemName: "square.and.pencil")
}
.padding(.trailing, 20)
}
ToolbarItem(placement: .topBarTrailing) {
Button(action: {
showDeleteConfirmationDialog = true
}) {
Image(systemName: "trash")
}
.confirmationDialog(
"Soll \(player.playerName) wirklich gelöscht werden?",
isPresented: $showDeleteConfirmationDialog,
titleVisibility: .visible
) {
Button("Ja") {
SQLiteManager.shared.deletePlayer(playerId: player.id)
players = SQLiteManager.shared.loadPlayersOfTeam(team: team, orderBy: playerOrderByType)
presentationMode.wrappedValue.dismiss()
}
Button("Nein", role: .destructive) { }
}
.padding(.trailing, 20)
}
}
.onAppear() {
player = SQLiteManager.shared.loadPlayer(playerId: playerId)[0]
}
}
}
对整个代码感到抱歉,但我相信更容易找出我的错误。
再次非常感谢。 斯文
我必须使用 for: 而不是 isPresented。这就是问题所在。
这是我的解决方案。
Button(action: {
navPath.append(1)
}) {
Text("Alle Spieler anzeigen")
Image(systemName: "person.3.fill")
}
2 导航目的地
.navigationDestination(for: TeamData.self) { team in
PlayerViewiPad(team: team)
}
.navigationDestination(for: Int.self) { noTeam in
PlayerViewiPad(team: TeamEmptyData)
}