Creating a Custom Swipe-to-Delete Action for a Cart Page UI with SwiftUI

To create a Stylish Shopping Cart UI Screens Using SwiftUI

In this tutorial, we will be building a custom cart page UI in SwiftUI, complete with swipe to delete functionality. We will use SwiftUI's powerful swipe gesture recognizer to create a sleek and intuitive user experience for removing items from the cart. By the end of the tutorial, you will have a solid understanding of how to use swipe gesture recognizers in SwiftUI to create interactive and engaging UI elements. You will also be able to apply the knowledge gained to other projects that require similar functionality.

Step 1: 
We need to create data class / struct 
Structs are complex data types, meaning that they are made up of multiple values. You then create an instance of the struct and fill in its values, then you can pass it around as a single value in your code. 

import SwiftUI

struct Item: Identifiable {
    var id = UUID().uuidString
    var name : String
    var details: String
    var image: String
    var price: Float
    var quantity: Int
    var offset: CGFloat
    var isSwiped: Bool
}


Step 2: 
The class needs to conform to the ObservableObject protocol from the Combine framework
This property publish any changes to its data, so that this change is updated in the view.

import SwiftUI

class CartViewModel: ObservableObject {
    @Published var items = [
        Item(name: "Tek Product 1", details: "Gift", image: "tek1", price: 10.00, quantity: 1, offset: 0, isSwiped: false),
        Item(name: "Tek Product 2", details: "Gift", image: "tek2", price: 90.99, quantity: 2, offset: 0, isSwiped: false),
        Item(name: "Tek Product 3", details: "Gift", image: "tek3", price: 100.00, quantity: 1, offset: 0, isSwiped: false),
        Item(name: "Tek Product 4", details: "Royal", image: "tek4", price: 50.00, quantity: 1, offset: 0, isSwiped: false),
        Item(name: "Tek Product 5", details: "Royal", image: "tek5", price: 100.00, quantity: 1, offset: 0, isSwiped: false)
    ]
}


Step 3:
To use custom font in our project : update info.plist file

Step 4:
This is our cart page, we divide 3 parts like header, body (LazyVStack) & bottom view.
import SwiftUI

struct CartView: View {
    @StateObject var cartData = CartViewModel()
    var body: some View {
        VStack {
            HStack(spacing: 20) {
                Button(action: {}) {
                    Image(systemName: "chevron.left")
                        .font(.system(size: 26, weight: .heavy))
                        .foregroundColor(.black)
                }
                
                Text("My cart")
                    //.font(.title2)
                    .fontWeight(.heavy)
                    .font(.custom("Jost-Bold", size: 28))
                
        
                Spacer()
            }
            .padding()
            
            ScrollView(.vertical, showsIndicators: false) {
                LazyVStack(spacing: 0) {
                    ForEach(cartData.items){ item in
                        // ItemView
                        ItemView(item: $cartData.items[getIndex(item: item)], items: $cartData.items)
                    }
                }
            }
            
            // Bottom View
            VStack {
                HStack {
                    Text("Total")
                        .fontWeight(.heavy)
                        .font(.custom("Jost-Bold", size: 28))
                        .foregroundColor(.gray)
                    
                    Spacer()
                    
                    // Calculating Total Price
                    Text(calculateTotalPrice())
                       // .font(.title)
                        .fontWeight(.heavy)
                        .font(.custom("Jost-Bold", size: 28))
                        .foregroundColor(.black)
                }
                .padding([.top, .horizontal])
                
                Button(action: {}) {
                    Text("Check out")
                        //.font(.title2)
                        .fontWeight(.heavy)
                        .font(.custom("Jost-Bold", size: 28))
                        .foregroundColor(.white)
                        .padding(.vertical)
                        .frame(width: UIScreen.main.bounds.width - 30)
                        .background(
                            LinearGradient(gradient: .init(colors: [Color("lightblue"), Color("blue")]), startPoint: .leading, endPoint: .trailing)
                        )
                        .cornerRadius(15)
                }
            }
        }
        .background(Color("gray").ignoresSafeArea())
    }
    
    func getIndex(item: Item) -> Int {
        return cartData.items.firstIndex { (item1) -> Bool in
            return item.id == item1.id
        } ?? 0
    }
    
    func calculateTotalPrice() -> String {
        var price: Float = 0
        
        cartData.items.forEach { (item) in
            price += Float(item.quantity) * item.price
        }
        
        return getPrice(value: price)
    }
}


Step 5:
Here is my item UI design 

import SwiftUI

struct ItemView: View {
    // For realtime updates
    @Binding var item: Item
    @Binding var items: [Item]
    var body: some View {
        ZStack {
            LinearGradient(gradient: .init(colors: [Color("lightblue"), Color("blue")]), startPoint: .leading, endPoint: .trailing)
            
            // Delete Button
            
            HStack {
                Spacer()
                
                Button(action: {
                    withAnimation(.easeIn){deleteItem()}
                }) {
                    Image(systemName: "trash")
                        .font(.title)
                        .foregroundColor(.white)
                        .frame(width: 90, height: 50)
                }
            }
            
            HStack(spacing: 15) {
                Image(item.image)
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: 130, height: 130)
                    .cornerRadius(18)
                
                VStack(alignment: .leading, spacing: 10) {
                    Text(item.name)
                        .fontWeight(.semibold)
                        .font(.custom("Jost-Bold", size: 18))
                        .foregroundColor(.black)
                    
                    Text(item.details)
                        .fontWeight(.semibold)
                        .font(.custom("Jost-Regular", size: 18))
                        .foregroundColor(.gray)
                    
                    HStack(spacing: 15) {
                        Text(getPrice(value: item.price))
                            .font(.subheadline)
                            .fontWeight(.heavy)
                            .font(.custom("Jost-Bold", size: 15))
                            .foregroundColor(.red)
                        
                        Spacer(minLength: 0)
                        
                        // Add - Subtract Button
                        Button(action: {
                            if item.quantity > 1 { item.quantity -= 1 }
                        }) {
                            Image(systemName: "minus")
                                .font(.system(size: 16, weight: .heavy))
                                .foregroundColor(.black)
                        }
                        
                        Text("\(item.quantity)")
                            .fontWeight(.heavy)
                            .font(.custom("Jost-Regular", size: 18))
                            .foregroundColor(.black)
                            .padding(.vertical, 5)
                            .padding(.horizontal, 10)
                            .background(Color.black.opacity(0.06))
                        
                        Button(action: {item.quantity += 1}) {
                            Image(systemName: "plus")
                                .font(.system(size: 16, weight: .heavy))
                                .foregroundColor(.black)
                        }
                    }
                }
            }
            .padding()
            .background(Color("gray"))
            .contentShape(Rectangle())
            .offset(x: item.offset)
            .gesture(DragGesture().onChanged(onChanged(value:)).onEnded(onEnd(value:)))
        }
    }
    
    func onChanged(value: DragGesture.Value) {
        if value.translation.width < 0 {
            if item.isSwiped {
                item.offset = value.translation.width - 90
            } else {
                item.offset = value.translation.width
            }
        }
    }
    
    func onEnd(value: DragGesture.Value) {
        withAnimation(.easeOut) {
            if value.translation.width < 0 {
                if -value.translation.width > UIScreen.main.bounds.width / 2 {
                    item.offset = -1000
                    deleteItem()
                } else if -item.offset > 50 {
                    item.isSwiped = true
                    item.offset = -90
                } else {
                    item.isSwiped = false
                    item.offset = 0
                }
            } else {
                item.isSwiped = false
                item.offset = 0
            }
        }
    }
    
    // Removing Item
    func deleteItem() {
        items.removeAll { (item) -> Bool in
            return self.item.id == item.id
        }
    }
}

func getPrice(value: Float) -> String {
    let format = NumberFormatter()
    format.numberStyle = .currency
    
    return format.string(from: NSNumber(value: value)) ?? ""
}


..

GET source code on Github:

In this Video i'm going to show Stylish Cart Page UI With Custom Swipe To Delete Action Using SwiftUI

..

Comments