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)
}
}
}
}
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)