Search code examples
swiftswiftuidrag-and-dropcopy-paste

In SwiftUI and macOS how do I copy-and-paste and drag and drop multiple local file URLs into a scroll view


In my macOS app I need to allow the user get a list of absolute local file URLs by selecting them from Finder using copy-and-paste or drag-and-drop. I'll filter them by the extension ".raw" and they should go, I think, in an array of URL. My experience using an app that support this suggests that I should implement both. I do know how to code a file-picker but that's not what I want.

I've been using Dr. Google to search but what I have found doesn't come close to illustrate what I need are too complex for me to understand enough to use as a guide.

Here is an example of one from an on-line tutorial that kind of works as a C&P but it's far from what I need. It also produces this: "CLIENT ERROR: TUINSRemoteViewController does not override -viewServiceDidTerminateWithError: and thus cannot react to catastrophic errors beyond logging them" so it's not really good code.

struct DataChooserView: View {
    @State private var username = "@twostraws"
    var body: some View {
        VStack {
            TextField("Username", text: $username)
                .textFieldStyle(.roundedBorder)

            PasteButton(payloadType: String.self) { strings in
                guard let first = strings.first else { return }
                username = first
            }
            .buttonBorderShape(.capsule)
        }
        .padding()
    }
}

I'm inexperienced in Swift/SwiftUI but learning.


Solution

  • After a lot of digging I found that this works for drag & drop.

    Need to include:

    @Binding var imagePathList: [String] // Debug GOAt
    @Binding var imageShortNameList: [String]
    @State var url = URL.self
    @State var isTargeted = false  // ???
    

    I found in de-bugging that imagePathList.count immediately after the drop is processed returns the count - 1, that is the count was not yet updated after the last .append. That was unexpected!

    Image(nsImage: nsimg)
      .resizable()
      .scaledToFill()
      .aspectRatio(contentMode: .fit)
      .frame(width: 640, height: 480)
      .border(.green)
      .onDrop(of: [.fileURL], isTargeted: $isTargeted) { providers in
         imagePathList.removeAll()
         imageShortNameList.removeAll()
            for provider in providers {
            provider.loadObject(ofClass: URL.self) { url, error in
               if let url = url {
               if url.isFileURL && url.pathExtension == "raw"  {
                  imagePathList.append(url.absoluteString)
                  imageShortNameList.append(url.lastPathComponent)
               } else {
                   // Handle non-file URLs (e.g., directories)
                   print("Invalid URL: \(url)")
               }
            } else if let error = error {
                // Handle any errors that occur during URL loading
                print("Error loading URL: \(error.localizedDescription)")
            }
                           }
        }  // DEBUG BELOW
           print("First Image \(imageShortNameList.first ?? "")")
           print("Last Image  \(imageShortNameList.last ?? "")")
           return true
    } // attract user attention
    .border(isTargeted ? Color.red.opacity(1.0) : Color.clear, width: isTargeted ? 2 : 1)