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
..
Tags:
* SwiftUI