Mobile Development 11 min read

Implementing a Custom Swipe‑to‑Delete Interaction in SwiftUI Using Gesture Modifiers

This article demonstrates how to build a custom left‑swipe delete interaction for SwiftUI list cards by using DragGesture, state handling, visual cues, confirmation alerts, hint text, and haptic feedback, offering a more flexible alternative to the built‑in List deletion.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Implementing a Custom Swipe‑to‑Delete Interaction in SwiftUI Using Gesture Modifiers

In the previous chapter we introduced the MVVM pattern and a simple delete action triggered by SwiftUI's contextMenu on a long‑press; this method deletes an item by calling a ViewModel function with the item's UUID.

For card‑style lists a more common UX is a left‑to‑right swipe that reveals a delete action. The built‑in EditButton swipe often has limitations, so we implement a custom gesture‑based solution.

SwiftUI provides three main gestures: onTapGesture , LongPressGesture , and DragGesture . We start by attaching a DragGesture to the card view:

// Drag gesture
.gesture(
    DragGesture()
        .onChanged { value in
            // actions while dragging
        }
        .onEnded { value in
            // actions when drag ends
        }
)

We declare a @GestureState (or a regular @State ) called viewState to store the current translation:

@State var viewState = CGSize.zero

The card view receives an .offset modifier that only allows movement to the left:

// Allow dragging only from right to left
.offset(x: self.viewState.width < 0 ? self.viewState.width : 0)

During the drag we update viewState with the translation and reset it to .zero when the gesture ends:

.gesture(
    DragGesture()
        .onChanged { value in
            self.viewState = value.translation
        }
        .onEnded { value in
            self.viewState = .zero
        }
)

To decide when a swipe should trigger deletion we define a threshold and a Boolean flag:

@State var valueToBeDeleted: CGFloat = -75
@State var readyToBeDeleted: Bool = false

Inside the gesture we compare the current offset with the threshold:

self.readyToBeDeleted = self.viewState.width < self.valueToBeDeleted ? true : false

If the flag is true we change the card background to red, otherwise keep it white:

.background(self.readyToBeDeleted ? Color(.systemRed) : .white)

When the drag finishes we reset readyToBeDeleted to false so the UI returns to its normal state.

The actual removal is performed in the ViewModel. We add a method to fetch an item by its UUID and another to delete it:

// Get item by UUID
func getItemById(itemId: UUID) -> Model? {
    return models.first { $0.id == itemId } ?? nil
}

// Delete item
func deleteItem(itemId: UUID) { /* implementation */ }

Each CardView receives the ViewModel, the item’s UUID, and a computed item property that calls getItemById :

var viewModel: ViewModel
var itemId: UUID
var item: Model? { viewModel.getItemById(itemId: itemId) }

When the swipe passes the threshold we present a confirmation alert before calling deleteItem :

@State var showDeleteAlert: Bool = false

private var deleteAlert: Alert {
    Alert(
        title: Text(""),
        message: Text("确定要删除吗?"),
        primaryButton: .destructive(Text("确认")) { /* delete */ },
        secondaryButton: .cancel(Text("取消"))
    )
}

// Attach alert to the card view
.alert(isPresented: $showDeleteAlert) { deleteAlert }

We also add a hint text that becomes visible behind the card when it is swiped left:

HStack {
    Spacer()
    Text("左滑删除")
        .padding()
        .foregroundColor(Color(.systemGray))
}

To give tactile feedback we create a small utility struct:

import Foundation
import SwiftUI

struct Haptics {
    static func hapticSuccess() {
        let generator = UINotificationFeedbackGenerator()
        generator.notificationOccurred(.success)
    }
    static func hapticWarning() {
        let generator = UINotificationFeedbackGenerator()
        generator.notificationOccurred(.warning)
    }
}

During the swipe we call Haptics.hapticWarning() to signal a potentially destructive action.

By combining gesture handling, state management, visual cues, confirmation alerts, hint text, and haptic feedback we achieve a polished, custom swipe‑to‑delete experience that goes beyond the default List behavior, illustrating how SwiftUI can be extended when built‑in components are insufficient.

iOSSwiftSwiftUIGesturehapticsSwipe DeleteUI Feedback
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.