Drag and Drop List In SwiftUI
In this article, We will explore how to implement a Drag and Drop List View in SwiftUI
Before proceeding, please consider subscribing to our YOUTUBE CHANNEL
It gives us a lot of motivation to produce high-quality content for you guys.
if you are interested in watching the video tutorial, Check out below.
Let's create an item view for the list.
Create a FruitItemView
import SwiftUI
struct FruitItemView: View {
var title: String
var body: some View {
HStack {
Spacer()
Text(title)
Spacer()
}
.padding(.vertical, 40)
.background(.yellow)
.cornerRadius(20)
}
}
Create a list using ForEach
and pass the values from the fruits array.
import SwiftUI
struct ContentView: View {
@State private var draggedFruit: String?
@State private var fruits: [String] = ["APPLE", "GRAPES", "BANANA", "WATERMELON", "PEACH", "KIWI", "DRAGON FRUIT"]
var body: some View {
ScrollView(showsIndicators: false, content: {
VStack(spacing: 10, content: {
Spacer()
.frame(height: 40)
ForEach(fruits, id: \.self) { fruit in
FruitItemView(title: fruit)
}
Spacer()
})
.padding(.horizontal, 20)
})
.ignoresSafeArea()
.background(.black)
}
}
We need to implement, drag and drop feature on the item,
Let's add an onDrag
modifier on FruitItemView
.
This callback will return NSItemProvider
and set the draggedFruit
to currently selected fruit.
import SwiftUI
struct ContentView: View {
@State private var draggedFruit: String?
@State private var fruits: [String] = ["APPLE", "GRAPES", "BANANA", "WATERMELON", "PEACH", "KIWI", "DRAGON FRUIT"]
var body: some View {
ScrollView(showsIndicators: false, content: {
VStack(spacing: 10, content: {
Spacer()
.frame(height: 40)
ForEach(fruits, id: \.self) { fruit in
FruitItemView(title: fruit)
.onDrag({
self.draggedFruit = fruit
return NSItemProvider()
})
}
Spacer()
})
.padding(.horizontal, 20)
})
.ignoresSafeArea()
.background(.black)
}
}
As we only added the drag option, the drop is not yet working.
To make it work, we need a DropDelegate
like below. Which will keep the dragged item.
import SwiftUI
struct DropViewDelegate: DropDelegate {
let destinationItem: String
@Binding var fruits: [String]
@Binding var draggedItem: String?
func dropUpdated(info: DropInfo) -> DropProposal? {
return DropProposal(operation: .move)
}
func performDrop(info: DropInfo) -> Bool {
draggedItem = nil
return true
}
func dropEntered(info: DropInfo) {
if let draggedItem {
let fromIndex = fruits.firstIndex(of: draggedItem)
if let fromIndex {
let toIndex = fruits.firstIndex(of: destinationItem)
if let toIndex, fromIndex != toIndex {
withAnimation {
self.fruits.move(fromOffsets: IndexSet(integer: fromIndex), toOffset: (toIndex > fromIndex ? (toIndex + 1) : toIndex))
}
}
}
}
}
}
Add this delegate inside onDrop
callback of FruitItemView
import SwiftUI
struct ContentView: View {
@State private var draggedFruit: String?
@State private var fruits: [String] = ["APPLE", "GRAPES", "BANANA", "WATERMELON", "PEACH", "KIWI", "DRAGON FRUIT"]
var body: some View {
ScrollView(showsIndicators: false, content: {
VStack(spacing: 10, content: {
Spacer()
.frame(height: 40)
ForEach(fruits, id: \.self) { fruit in
FruitItemView(title: fruit)
.onDrag({
self.draggedFruit = fruit
return NSItemProvider()
})
.onDrop(of: [.text], delegate: DropViewDelegate(destinationItem: fruit, fruits: $fruits, draggedItem: $draggedFruit))
}
Spacer()
})
.padding(.horizontal, 20)
})
.ignoresSafeArea()
.background(.black)
}
}
That’s it, the drop is added to the list item
Once again Thanks for stopping by.
Do check out our YOUTUBE CHANNEL
Social Handles
Instagram : https://www.instagram.com/mobileappsacademy/
Twitter : https://twitter.com/MobileAppsAcdmy
LinkedIn : https://www.linkedin.com/company/mobile-apps-academy/