2

I have Picker of style SegmentedPickerStyle(). Now i'm doing some calculation based on picker value, and it's working. But somehow my picker is not changing its selection value if I tap on it. For changing the selection value, i need to swipe over the values.
Another issue is, if i need to calculate TipAmount then first i need to swipe to the picker value and then need to select that value. I don't know why is it happening!!

Here is my View code,

struct ContentView: View {

    @ObservedObject var tipViewModel = TipViewModel()

    var body: some View {

        VStack {

            TextField("Enter bill amount", text: $tipViewModel.billAmount)
                .textFieldStyle(RoundedBorderTextFieldStyle())

            Picker(selection: $tipViewModel.tipPercentage, label: Text("Select tip %")) {
                ForEach(tipViewModel.tipChoices, id: \.self) { choice in
                    //Text("\(self.tipViewModel.tipChoices[choice])")
                    Text("\(choice)")
                }
            }.pickerStyle(SegmentedPickerStyle())
                .onTapGesture {
                    self.tipViewModel.calculateTip()
            }
            Text("Tip Percentage \(tipViewModel.tipPercentage)")
            Text(tipViewModel.tipAmount == nil ? "Tip Amount" : "\(tipViewModel.tipAmount!)")
        }.padding()
    }
}

And here is TipViewModel file,

class TipViewModel: ObservableObject {

    @Published var billAmount: String = "" {
        didSet{
            calculateTip()
        }
    }
    @Published var tipPercentage: Int = 10
    @Published var tipAmount: Double = 0

    let tipChoices = [10, 15, 20]

    let didChange = PassthroughSubject<TipViewModel, Never>()

    func calculateTip() {

        guard let billAmount = Double(billAmount) else { return }
        self.tipAmount = billAmount * Double(tipPercentage) / 100
        self.didChange.send(self)
    }
}

Any help would be appreciated!!

1 Answer 1

3

You have an onTapGesture modifier that is eating the taps gestures on the picker and trying to calculate the tip instead.

 .onTapGesture {
                self.tipViewModel.calculateTip()
        }

You can just calculate the tipAmount as a computed property:

struct ContentView: View {

    @ObservedObject var tipViewModel = TipViewModel()

    var body: some View {

        VStack {

            TextField("Enter bill amount", text: $tipViewModel.billAmount)
                .textFieldStyle(RoundedBorderTextFieldStyle())

            Picker(selection: $tipViewModel.tipPercentage, label: Text("Select tip %")) {
                ForEach(tipViewModel.tipChoices, id: \.self) { choice in
                    //Text("\(self.tipViewModel.tipChoices[choice])")
                    Text("\(choice)")
                }
            }.pickerStyle(SegmentedPickerStyle())

            Text("Tip Percentage \(tipViewModel.tipPercentage)")
            Text("\(tipViewModel.tipAmount)")
        }.padding()
    }
}

class TipViewModel: ObservableObject {

    @Published var billAmount: String = ""
    @Published var tipPercentage: Int = 10

    var tipAmount: Double {
        guard let billAmount = Double(billAmount) else { return 0 }
        let tipAmount = billAmount * Double(tipPercentage) / 100
        return tipAmount
    }

    let tipChoices = [10, 15, 20]

}

Since you already have billAmount and tipPercentage as @Published properties you don't need to worry about sending a message to a PassthroughSubject to tell the view to update when the computed property changes. The willChange message will be sent when either the tip amount or the total amount are changed. That will cause the view to refresh, which will call the tipAmount computed property which will run its closure and return the new value to the view.

Sign up to request clarification or add additional context in comments.

1 Comment

yes you're right it's working now. i'm new to swiftUI so was bit confused about Published, ObservableObject, ObservedObject.. btw thanks you saved my time!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.