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.
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
}
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)
]
}
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)
}
}
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:
Comments
Post a Comment