Warm tip: This article is reproduced from stackoverflow.com, please click
swift swiftui watchos

Adding List Values in SwiftUI for TotalValue

发布于 2020-04-07 10:21:33

Question here In the process of learning SwiftUI, this app is specifically just for WatchOS. So I'm creating a row view that then uses a carousel view. I have it so if you click on the list item it asks you to input your score. That, of course, goes into a string (I wish I could get it to go to an int, but wouldn't work for me.) The frame score shows up fine. However, I'm trying to figure out a way to get the total score to add correctly.

For Example,

Frame 1 score 5 Total score 5

Frame 2 score 2 Total score 7

Frame 3 score 10 Total Score 17

...

Any help would be much appreciated thanks

struct StartBowlingView: View {
    var body: some View {
        List{
            RowView(title: "1", framescore: "0", totalscore: "0")
            RowView(title: "2", framescore: "0", totalscore: "0")
            RowView(title: "3", framescore: "0", totalscore: "0")
            RowView(title: "4", framescore: "0", totalscore: "0")
            RowView(title: "5", framescore: "0", totalscore: "0")
            RowView(title: "6", framescore: "0", totalscore: "0")
            RowView(title: "7", framescore: "0", totalscore: "0")
            RowView(title: "8", framescore: "0", totalscore: "0")
            RowView(title: "9", framescore: "0", totalscore: "0")
            RowView(title: "10", framescore: "0", totalscore: "0")
        }
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
        .navigationBarTitle("Frame")
        .listStyle(CarouselListStyle())
    }
}

struct RowView: View {
    @State var title: String
    @State var framescore:  String
    @State var totalscore: String

    var TotalScore: Int {
        let Totalframescore = Int(framescore) ?? 0
        return Totalframescore
    }

    var body: some View {
        NavigationLink(destination: TextField("Enter your Frame Score", text: $framescore) .border(Color.black))
        { //Start Frame List Design View
            VStack(alignment: .leading) {
                HStack(alignment: .center) {
                    Text(title)
                        .font(.system(.headline, design: .rounded))
                        .foregroundColor(Color.blue)
                        .multilineTextAlignment(.leading)
                    Divider()
                    Spacer()
                    Text("\(framescore)")
                        .font(.system(.body, design: .rounded))
                        .multilineTextAlignment(.leading)
                    Divider()
                    Spacer()
                    Text("\(TotalScore)")
                        .font(.system(.headline, design: .rounded))
                        .foregroundColor(Color.green)
                        .multilineTextAlignment(.trailing)
                }
            }
            .listRowBackground(Color.blue)
            .frame(height: 60, alignment: .topTrailing)
        }
    }
}
Questioner
SwiftArseid
Viewed
85
Ben 2020-02-01 20:37

one approach could be on each row to reduce the set of scores up to that point. By iterating across the set of scores, it can count the frame number, the frame score, and the total of the previous scores. For example, your main view could look like this:

struct StartBowlingView: View {

    @State var frameScores = [5, 2, 10]

    var body: some View {
        List{
            ForEach(0..<self.frameScores.endIndex) { index in
                RowView(title: "\(index + 1)",
                    framescore: "\(self.frameScores[index])",
                    totalscore: "\(self.frameScores[0...index].reduce(0, { $0 + $1 }))")
            }
        }
    }
}

I realize bowling scores can be more complicated than simply summing the frames (though I do not know the exact logic), but another approach could be to create a frame model to track more than a single Int:

struct FrameScore {
    let frameNumber: Int
    let firstScore: Int
    let secondScore: Int
    let previousScore: Int

    var frameScore: Int { return self.firstScore + self.secondScore }
    var totalScore: Int { return self.frameScore + self.previousScore }

    var isStrike: Bool { return self.firstScore == 10 }
    var isSpare: Bool { return !self.isStrike && self.frameScore == 10 }
}

And then the main view could update to keep a list of frames:

struct StartBowlingView: View {

    @State var frames = [FrameScore]()

    var body: some View {
        List{
            ForEach(self.frames, id: \.frameNumber) { frame in
                RowView(title: "\(frame.frameNumber)",
                    framescore: "\(frame.frameScore)",
                    totalscore: "\(frame.totalScore)")
            }

            Button("Add Score") {
                let first = Int.random(in: 0...10)
                let second = Int.random(in: 0...(10 - first))

                // Here's where to add some logic about the prevous frames' strikes and spares affecting the new frame/total score

                self.frames.append(
                    FrameScore(frameNumber: self.frames.count + 1,
                               firstScore: first,
                               secondScore: second,
                               previousScore: self.frames.last?.totalScore ?? 0))
            }
        }
    }
}