I have the simple add function which can calculate the total . It working fine . I want to update grand total when and - is clicked form stepper . Not sure How can I pass the total variable into stepper ..
Here is the modal ..
import Foundation
// MARK: - Welcome
struct ProductData: Codable, Hashable {
let carts: [Cart]
}
// MARK: - Cart
struct Cart: Codable, Hashable {
let id: Int
let products: [Product]
}
// MARK: - Product
struct Product: Codable, Hashable, Identifiable {
let id: Int
let title: String
var price, quantity, total: Int
let discountPercentage: Double
let discountedPrice: Int
let thumbnail: String
}
Here is my class ..
import SwiftUI
class Order: ObservableObject {
@Published var products = [Product]()
@Published private(set) var productTotal: Int = 0
func add(item: Product) {
if let index = products.firstIndex(where: {$0.id == item.id }) {
products[index].quantity = 1
productTotal = item.quantity * item.price
} else {
products.append(item)
productTotal = item.quantity * item.price
}
}
func remove(item: Product) {
if let index = products.firstIndex(of: item) {
products.remove(at: index)
productTotal -= item.quantity * item.price
}
}
}
Be more specific.. Here is the Stepper code ..
VStack {
HStack {
Stepper {
Text("\(Image(systemName: "multiply"))\(item.quantity)")
}
onIncrement: {
item.quantity = 1
}
onDecrement: {
if item.quantity > 1 {
item.quantity -= 1
}
}
Image(systemName: "trash")
.foregroundColor(Color(hue: 1.0, saturation:0.89, brightness: 0.835))
.onTapGesture {
order.remove(item: item)
}
}
}
Here is the view code for calculating total..
HStack {
Text("Your cart total is :")
.bold()
Spacer()
Text("£\(order.productTotal).00")
.bold()
}
.padding()
Here is complete my view code ..
import SwiftUI
struct OrderView: View {
@EnvironmentObject var order: Order
var body: some View {
NavigationStack {
List {
Section {
ForEach.init($order.products) { $item in
Section {
HStack {
Text("Description")
.foregroundColor(.blue)
.bold()
Spacer()
Text("Action")
.foregroundColor(.blue)
.bold()
}
}
HStack {
if let url = URL(string: item.thumbnail) {
ProductAsyncImageView(url: url)
.frame(width: 90, height: 90)
.padding(.bottom, -10)
.clipShape(Circle())
}
Spacer()
VStack {
HStack {
Stepper {
Text("\(Image(systemName: "multiply"))\(item.quantity)")
}
onIncrement: {
item.quantity = 1
// increment function
}
onDecrement: {
if item.quantity > 1 {
item.quantity -= 1
// decrement function
}
}
Image(systemName: "trash")
.foregroundColor(Color(hue: 1.0, saturation: 0.89, brightness: 0.835))
.onTapGesture {
order.remove(item: item)
}
}
}
Spacer()
}
VStack {
HStack(spacing: 20) {
Text("Name: \(item.title)")
.font(.subheadline)
.frame(maxWidth: .infinity, alignment: .leading)
.bold()
}
HStack {
Text("Price: £\(item.price)")
.font(.subheadline)
.bold()
.frame(maxWidth: .infinity, alignment: .leading)
Text("Sub Total: £\(item.quantity * item.price)")
.font(.subheadline)
.bold()
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}
HStack {
Text("Your cart total is :")
.bold()
Spacer()
Text("£\(order.productTotal).00")
.bold()
}
.padding()
}
Section {
NavigationLink("Place Order") {
}
}
}
.navigationTitle("Order")
}
}
}
CodePudding user response:
The simplest way is probably to add a function to your model - Also, an Int
probably isn't a good choice for representing currency. NSDecimalNumber
is best, but for simplicity we can use a Double
.
class Order: ObservableObject {
@Published var products = [Product]()
@Published private(set) var productTotal: Double = 0
func add(item: Product) {
if let index = products.firstIndex(where: {$0.id == item.id }) {
products[index].quantity = 1
} else {
products.append(item)
}
self.calculateTotal()
}
func remove(item: Product) {
if let index = products.firstIndex(of: item) {
products.remove(at: index)
self.calculateTotal()
}
}
func calculateTotal() {
self.productTotal = self.products.reduce(0.0, { total, item in
total Double(item.quantity)*item.price
}
}
}
You can also use this function when you increment or decrement an item quantity:
Stepper {
Text("\(Image(systemName: "multiply"))\(item.quantity)")
}
onIncrement: {
item.quantity = 1
self.order.calculateTotal()
}
onDecrement: {
if item.quantity > 1 {
item.quantity -= 1
self.order.calculateTotal()
}
}
CodePudding user response:
Your productTotal
should be a computed property. Change your Order
class to this and it should work:
class Order: ObservableObject {
@Published var products = [Product]()
var productTotal: Int {
products.map { $0.quantity * $0.price }.reduce(0, )
}
func add(item: Product) {
if let index = products.firstIndex(where: {$0.id == item.id }) {
products[index].quantity = 1
} else {
products.append(item)
}
}
func remove(item: Product) {
if let index = products.firstIndex(of: item) {
products.remove(at: index)
}
}
}