Add haptic to drag and drop (draggable, dropDestination)

118 views Asked by At

How can I add haptic effect on a drag and drop ? I follow this tutorial: https://www.youtube.com/watch?v=UFiOCcm6zTo but I can't figure out how to add haptic when we select a view and drop it.

thanks

LazyVGrid(columns: columns, spacing: 10, content: {
    ForEach(cards) { card in
        
        CardView(numberOfColumns: CGFloat(numberOfColumns), is2ColumnsSelected: is2ColumnsSelected, size: size, card: card)
            .draggable(card) {
                CardView(numberOfColumns: CGFloat(numberOfColumns), is2ColumnsSelected: is2ColumnsSelected, size: size, card: card)
                    .frame(width: size.width/CGFloat(numberOfColumns) - 20)
                    .onAppear {
                        draggingCard = card
                    }
            }
            .dropDestination(for: Card.self) { items, location in
                draggingCard = nil
                return false
            } isTargeted: { status in
                if let draggingCard, status, draggingCard != card {
                    if let sourceIndex = cards.firstIndex(of: draggingCard), let destinationIndex = cards.firstIndex(of: card) {
                        withAnimation(.bouncy) {
                            let sourceCard = cards.remove(at: sourceIndex)
                            cards.insert(sourceCard, at: destinationIndex)
                        }
                    }
                }
            }
    }
}
    

I tried to ad haptic in the draggable modifier and dropDestination modifier

CardView(numberOfColumns: CGFloat(numberOfColumns), is2ColumnsSelected: is2ColumnsSelected, size: size, card: card)
    .draggable(card) {
        UIImpactFeedbackGenerator(style: .medium).impactOccurred()
        CardView(numberOfColumns: CGFloat(numberOfColumns), is2ColumnsSelected: is2ColumnsSelected, size: size, card: card)
            .frame(width: size.width / CGFloat(numberOfColumns) - 20)
            .onAppear {
                draggingCard = card
            }
    }
    .dropDestination(for: Card.self) { _, _ in
        draggingCard = nil
        return false
    } isTargeted: { status in
        if let draggingCard, status, draggingCard != card {
            if let sourceIndex = cards.firstIndex(of: draggingCard), let destinationIndex = cards.firstIndex(of: card) {
                withAnimation(.bouncy) {
                    let sourceCard = cards.remove(at: sourceIndex)
                    cards.insert(sourceCard, at: destinationIndex)
                }
            }
        }
    }
1

There are 1 answers

0
Sweeper On BEST ANSWER

Use the sensoryFeedback(_:trigger:) modifier to add haptics. You just need to declare a new @State as the trigger:, so that whenever the state changes, the haptics gets triggered.

// suppose you want different haptics for when the user starts dragging
// and for when a reorder of the items happens due to the drag
@State var dragTrigger = false
@State var reorderTrigger = false

// let's also suppose that we have a grid of Colors
@State var colors = [Color.red, .green, .blue, .purple, .yellow, .orange, .pink, .brown]
@State var draggedItem: Color?

Then, all you need to do is to insert dragTrigger.toggle() and reorderTrigger.toggle() at appropriate places, and add the sensoryFeedback

RoundedRectangle(cornerRadius: 10)
    .fill(color)
    .draggable(color) {
        RoundedRectangle(cornerRadius: 10)
            .fill(.ultraThinMaterial)
            .frame(width: geo.size.width, height: geo.size.height)
            .onAppear {
                dragTrigger.toggle()
                draggedItem = color
            }
    }
    .dropDestination(for: Color.self) { items, location in
        draggedItem = nil
        return false
    } isTargeted: { status in
        if let draggedItem, status, draggedItem != color {
            if let sourceIndex = colors.firstIndex(of: draggedItem), let destinationIndex = colors.firstIndex(of: color) {
                reorderTrigger.toggle()
                withAnimation(.bouncy) {
                    let sourceItem = colors.remove(at: sourceIndex)
                    colors.insert(sourceItem, at: destinationIndex)
                }
            }
        }
    }
// add these to the LazyVGrid
.sensoryFeedback(.selection, trigger: dragTrigger)
.sensoryFeedback(.impact, trigger: reorderTrigger)