问题
I'm currently encountering problems when changing a binded value inside of a navigation link.
The data structure looks like this:
internal struct DailyNutritionReportElement: Identifiable, Hashable, Codable {
internal var id: UUID
internal var amount: Double
internal var comment: String
internal var dailyNutritionReportMO: DailyNutritionReport?
internal var dailyNutritionReportElementTypeMO: DailyNutritionReportElementType
internal var isDone: Bool
}
In my main view I then show different Elements in a ScrollView:
ScrollView {
VStack(spacing: 15) {
ForEach(self.viewModel
.dailyNutritionReports[self.selectedReportIndex]
.dailyNutritionReportElementMO,
id: \.self) { element in
ReportElementItem(
viewModel: self.viewModel,
element: element,
selectedReportIndex: self.selectedReportIndex
)
}
}
}
For Each Element I show a ReportElementItem which looks like this:
struct ReportElementItem: View {
@ObservedObject var viewModel: HomeViewVielModel
@State var element: DailyNutritionReportElement
@State private var isChecked: Bool = false
var selectedReportIndex: Int
var body: some View {
if element.dailyNutritionReportElementTypeMO.dailyNutritionReportElementUnitMO.abbreviation == "Y/N" {
BooleanReportElementItem(
typeDescription: element.dailyNutritionReportElementTypeMO.typeDescription,
icon: element.dailyNutritionReportElementTypeMO.dailyNutritionReportElementTypeIconMO,
toggleState: self.getToggleBinding()
)
}
if element.dailyNutritionReportElementTypeMO.dailyNutritionReportElementUnitMO.abbreviation != "Y/N" {
MeasureableReportElement(
icon: element.dailyNutritionReportElementTypeMO.dailyNutritionReportElementTypeIconMO,
typeDescription: element.dailyNutritionReportElementTypeMO.typeDescription,
amount: self.amountBinding(),
target: element.dailyNutritionReportElementTypeMO.dailyTarget,
unit: element.dailyNutritionReportElementTypeMO.dailyNutritionReportElementUnitMO
)
}
}
private func amountBinding() -> Binding<Double> {
Binding(
get: { element.amount },
set: {
element.amount = $0
self.viewModel.updateDailyNutritionReportElement(element: element)
}
)
}
private func getToggleBinding() -> Binding<Bool> {
Binding<Bool>(
get: { self.element.isDone },
set: {
self.element.isDone = $0
self.viewModel.updateDailyNutritionReportElement(element: element)
}
)
}
}
In there I show a MeasureableReportElement which looks like this:
{
@State var icon: DailyNutritionReportElementTypeIcon
@State var typeDescription: String
@Binding var amount: Double
@State var target: Double
@State var unit: DailyNutritionReportElementUnit
var body: some View {
NavigationLink(
destination: ProgressBar(
progress: $amount,
target: target,
typeDescription: typeDescription
)
) {
Group {
VStack(spacing: 10) {
Text(typeDescription)
.foregroundColor(Color.label)
.font(.system(size: 20))
HStack {
Text("\(String(format: "%.2f", amount))")
Text("von")
Text("\(String(format: "%.2f", target))")
Text(unit.name)
}
.foregroundColor(Color.label)
}
Spacer()
}
.cardified(with: icon)
}
}
}
And finally in there I'm navigating to the ProgressBar via NavigationLink, which looks like this:
struct ProgressBar: View {
@Binding var progress: Double
@State var target: Double
@State var typeDescription: String
@State var rotationAngle: Angle = Angle.degrees(0)
@Environment(\.colorScheme) var colorScheme: ColorScheme
var body: some View {
VStack {
if self.isTargetReached() {
Text("You did it 😊🎉")
.font(.system(size: 30))
.padding(.top, 30)
}
ZStack {
Circle()
.fill(self.isTargetReached() ? Color.green : Color.systemBackground)
Circle()
.stroke(lineWidth: 20.0)
.opacity(0.3)
.foregroundColor(self.getCircleColor())
Circle()
.trim(
from: 0.0,
to: CGFloat(min(self.progress / self.target, 1.0))
)
.stroke(
style: StrokeStyle(
lineWidth: 20.0,
lineCap: .round,
lineJoin: .round
)
)
.foregroundColor(self.getCircleColor())
.rotationEffect(Angle(degrees: 270.0))
VStack {
if self.isTargetReached() {
Image(systemName: "checkmark.circle")
.font(.system(size: 120))
.foregroundColor(isTargetReached() ? Color.white : Color.label)
}
HStack {
Text("\(String(format: "%.2f", self.progress))")
Text("/")
Text("\(String(format: "%.2f", self.target))")
}
.animation(.none)
.padding()
.foregroundColor(isTargetReached() ? Color.white : Color.label)
}
.font(.largeTitle)
}
.padding()
.rotationEffect(self.rotationAngle)
HStack {
Spacer()
Circle()
.frame(
width: 80,
height: 80,
alignment: .center
)
.overlay(
Image(systemName: "minus")
.font(.system(size: 30))
.foregroundColor(.white)
)
.foregroundColor(.red)
.onTapGesture {
if self.progress > 0 {
withAnimation(.default) {
self.progress -= 0.25
}
}
}
Spacer()
Circle()
.frame(
width: 80,
height: 90,
alignment: .center
)
.overlay(
Image(systemName: "plus")
.font(.system(size: 30))
.foregroundColor(.white)
)
.foregroundColor(getCircleColor())
.onTapGesture {
withAnimation(.default) {
self.progress += 0.25
}
if target == progress {
withAnimation {
self.rotationAngle += Angle.degrees(360)
}
}
}
Spacer()
}
.padding(.bottom, 50)
}
.navigationBarTitle("\(typeDescription)", displayMode: .inline)
}
private func isTargetReached() -> Bool {
self.progress >= self.target ? true : false
}
private func getCircleColor() -> Color {
isTargetReached() ? Color.green : Color.blue
}
}
Everytime I press a Button in the ProgressBar-View, the progress value changes, which binds the element.amount value in ReportElementItem. This part works like intended.
Now to the part that I don't understand:
Everytime I press one of the Buttons in the ProgressBar-View the Navigation jumps back one step automatically and the View disappears. The same happens when I use a sheet instead of a NavigationLink and I can't understand that.
Does anybody know this problem? I hope my description is detailed enough. If somebody needs more code or a video/gif of the problem, just let me know.
来源:https://stackoverflow.com/questions/64031011/swiftui-change-of-binding-forces-navigationview-to-go-back-automatically