Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the drop target background effect in sidebars on macOS using SwiftUI?

I have a SwiftUI List that's a sidebar on macOS. For its items I have added the dropDesternation modifier like this:

.dropDestination(for: URL.self) { urls, _ in
      for url in urls {
          //... adding urls to destination
      }
}

return true
} isTargeted: { inDropArea in
      if inDropArea {
          highlightedItem = item
      } else {
          highlightedItem = nil
    }
}
       

By default if the cursor is above the item I get no effect, but I want the same effect like using NSOutlineView in AppKit. Here's an example from the Finder:

Drag and drop in the Finder sidebar

As you can see I have implemented highlightedItem in the code above. I can use it to check if an item is targeted and draw a background:

 .background {
       if item == highlightedItem {
            RoundedRectangle(cornerRadius: 5)
            .fill(Color.blue)
            .frame(maxWidth: .infinity, maxHeight: .infinity)
       }
}

But that does not look quite the same:

own implementation of sidebar drop target

Interestingly the effect I want is the same you get if you use a selection for the sidebar list like: List(selection: $selectedItem)

There must be a native way to do this so I don't have to fake it and get something that does not look quite right.

like image 787
Daniel Avatar asked Oct 20 '25 14:10

Daniel


1 Answers

Having the List elements be selectable and triggering that selection temporarily seems to work quite nicely.

For example ...

let Bands: Array<String> = [
    "Beatles", "Rolling Stones", "Animals", "Beach Boys", "Doors",
]

struct ContentView: View {
    @State var selection: String? = nil
    @State var dropSelectionCache: String? = nil

    var body: some View {
        VStack {
            Text("Drag text on onto band to trigger highlighting")
            List(Bands, id: \.self, selection: $selection) { band in
                Text(band)
                    .dropDestination(for: String.self) { _, _ in
                        true
                    } isTargeted: { (isTarget: Bool) in
                        if isTarget {
                            dropSelectionCache = selection
                            withAnimation {
                                selection = band
                            }
                        } else {
                            withAnimation {
                                selection = dropSelectionCache
                            }
                        }
                    }
            }
        }
    }
}

Here's a gif illustrating the code in operation on Ventura (apologies for slightly shakey screen recording - that's me an and not the code :-/ )

Low res drag and drop showing code in operation

like image 95
shufflingb Avatar answered Oct 23 '25 08:10

shufflingb



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!