Frank R
Frank R

Reputation: 891

SwiftUI: Adding .onDrag blocks clicking/selecting an item in the macOS sidebar

I have a list in my macOS app’s sidebar. When I add the .onDrag modifier, dragging NSItemProvider works correctly, but clicking/selecting the item to trigger the NavigationLink gets blocked.

struct ContentView: View {
    var body: some View {
        NavigationView {
            List(Data.items) { item in
                NavigationLink(
                    destination: Text(item.text),
                    label: {
                        Text(item.text)
                          .lineLimit(5)
                           // Enabling dragging with .onDrag {} disables click and selection:
                          .onDrag { return NSItemProvider(object: item.url as NSURL) }
                    })
            }
            Text("Placeholder")
        }
    }
}

How can I add the drag behavior while keeping the normal list selection behavior intact?

Here ist the rest of the Minimal Reproducible Example:

import SwiftUI

@main
struct ListDragExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct Data {
    struct Item: Identifiable {
        let id = UUID()
        let text = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
        let url = URL(string: "http://example.com")!
    }
    static let items = [
        Item(),
        Item(),
        Item()
    ]
}

Upvotes: 14

Views: 1054

Answers (1)

Alvaro Costa Neto
Alvaro Costa Neto

Reputation: 61

I am having the same issue, and from what I could find, it's actually a bug in SwiftUI. I may be able to contribute some more details to the discussion:

  • It's not directly related to a NavigationLink. I have a plain List with custom item views and it's presenting the same erratic behavior;
  • In my custom item view, there are areas that don't have an actual View in them, such as the blank spaces beside Text views. When I click on those areas (the "background", see the image below), I can still select the item, but the dragging mechanism doesn't work;
  • When dropping a dragged an item on another, the area that accepts the drop is comprised only of the frames of the destination item's subviews. If I try to drop on the "background" area of the List item, it won't accept it.

Since I don't have enough reputation yet, please click this link to see the image.

Overall, it seems to me that whenever the item that accepts either drags or drops has subviews—being a simple Text or a more complex layout, such as the one I showed in the picture—the dragging mechanism disables selection whenever the mouse cursor hits inside one of those subviews, while only allowing drags and drops to occur by starting or ending inside their frames.

On the code below, the selection mechanism will work when clicking on the area to the right of the "Short" item, but the drag will only be triggered if it starts inside the Text view's frame.

struct ExampleList: View {
  private let data = ["Short", "Average", "Loooooooooooong"]
  @State private var selected = Set<String>()

  var body: some View {
    List(data, id: \.self, selection: $selected) { text in
      Text(text) // Clicking outside the frame selects it...
                 // ...but clicking inside starts the drag mechanism.
        .onDrag { NSItemProvider(object: text as NSString) }
    }
  }
}

Hope this helps. Cheers from Brazil!

Upvotes: 6

Related Questions