SwiftUI drag and drop out of scope

Viewed 30

I have the following MacOS app written in SwiftUI. I am trying to write a drag and drop functionality, where I can drag some elements of the list from the right column (in this case Test or Test2), drop them on the text element that says empty, and change “empty” to the value of the dragged element (i.e. if I drag and drop Test2, the column on the left should say Test2. First of all, this is how I initialize the data.

struct MyArticles: Identifiable {
    var id = UUID()
    var title: String
}

let testData = [
    MyArticles(title: "Test"),
    MyArticles(title: "Test2")
]

Then this is the rest of the code

import SwiftUI

struct ContentView: View {
    @State private var dropText = "empty"
    var articles: [MyArticles] = []
    var body: some View {
        NavigationView {
            VStack(alignment: .leading) {
                Text(dropText)
                    .onDrop(of: [.text], delegate: DTDropTarget(dragText: $article.title, dropText: $dropText))
            }
            List(articles) { article in
                NavigationLink(destination: Text(article.title)) {
                VStack(alignment: .leading) {
                    Text(article.title)
                    }
                .onDrag{NSItemProvider(object: article.title as NSString)}
                }
            }
        }
    }
}

struct DTDropTarget: DropDelegate {
    @Binding var dragText: String
    @Binding var dropText: String
    
    func performDrop(info: DropInfo) -> Bool {
        dropText = String(describing: dragText)
        return true
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(articles:testData)
    }
}

The problem is on line 10, or the .onDrop function, it says Cannot find '$article' in scope. I am a complete beginner so any detailed explanation of what is going wrong would help me a lot.

1 Answers

First of all, articles in your code is an array so you need to specific which index is using and get name of article. But in here you don't need to do that because of when you drop the DropInfo contains the variable when you drag to drop.

Second, you don't need to make a new DropDelegate instead you can add DropDelegate into your view. When a text is dropped, it will call performDrop and change text depends on DropInfo

Code will be like this

struct MyArticles: Identifiable {
    var id = UUID()
    var title: String
}

struct ContentView: View, DropDelegate {
    @State private var dropText : String = "empty"
    var articles: [MyArticles] = [
        MyArticles(title: "Test"),
        MyArticles(title: "Test2")
    ]
    
    var body: some View {
        NavigationView {
            VStack(alignment: .leading) {
                Text(dropText)
                    .onDrop(of: [.text], delegate: self)
            }
            List(articles) { article in
                NavigationLink(destination: Text(article.title)) {
                VStack(alignment: .leading) {
                    Text(article.title)
                    }
                .onDrag{NSItemProvider(object: article.title as NSString)}
                }
            }
        }
    }
    
    func performDrop(info: DropInfo) -> Bool {
        // NSSTring on drag so we get value from "public.utf8-plain-text"
        if let item = info.itemProviders(for: ["public.utf8-plain-text"]).first
        {
            item.loadItem(forTypeIdentifier: "public.utf8-plain-text") {
                (data, error) in
                if let data = data as? Data {
                    dropText = (NSString(data: data, encoding: 4) ?? "empty") as String
                }
            }
        }
        
        return true
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

The result enter image description here

Related