Drag and Drop List In SwiftUI

Mobile Apps Academy
3 min readDec 29, 2023

--

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/

--

--

Mobile Apps Academy

Welcome to Mobile Apps Academy, your go-to channel for all things mobile app development!