SwiftUI - Change of Binding forces NavigationView to go back automatically

折月煮酒 提交于 2020-12-15 06:32:40

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!