3

We are using the OnDrop() function in SwiftUI for a MacOS application. It works really great. However, I want to allow the onDrop function only on a special condition. I tried executing the code after the onDrag only if that condition is true, which works fine. However, there is still the dragging animation / mouse drag effect visible which shouldn’t be visible.

That is the code we are using:

.onDrop(of: [“public.file-url”], isTargeted: $userData.shopPopOver) { providers -> Bool in
for provider in providers
{

Can I only add that .onDrop on a condition. Just to show an example, which surely is not working:

if (condition)
{
.onDrop(of: [“public.file-url”], isTargeted: $userData.shopPopOver) { providers -> Bool in

Thanks in advance!

Asperi
  • 228,894
  • 20
  • 464
  • 690
lvollmer
  • 1,418
  • 2
  • 13
  • 32

2 Answers2

7

Here is custom modifier that can be used for described use-case

struct Droppable: ViewModifier {
    let condition: Bool
    let types: [String]
    let tracking: Binding<Bool>?
    let action: ([NSItemProvider]) -> Bool

    @ViewBuilder
    func body(content: Content) -> some View {
        if condition {
            content.onDrop(of: types, isTargeted: tracking, perform: action)
        } else {
            content
        }
    }
}

extension View {
    public func acceptDrop(if condition: Bool, of supportedTypes: [String], isTargeted: Binding<Bool>?, perform action: @escaping ([NSItemProvider]) -> Bool) -> some View {
        self.modifier(Droppable(condition: condition, types: supportedTypes, tracking: isTargeted, action: action))
    }
}

Asperi
  • 228,894
  • 20
  • 464
  • 690
1

In addition to @Asperi's straightforward solution, I'd like to offer my modifier, which avoids to use a conditional view.

This may help in situations where you see performance issues with conditional views.

fileprivate struct Droppable: ViewModifier {
    let condition: Bool
    let types: [String]
    var conditionalTypes: [String]  { condition ? types : []  }
    let tracking: Binding<Bool>?
    let action: ([NSItemProvider]) -> Bool
    
    @ViewBuilder
    func body(content: Content) -> some View {
        content.onDrop(of: conditionalTypes, isTargeted: tracking, perform: action)
    }
}

extension View {
    public func onDrop(if condition: Bool, of supportedTypes: [String], isTargeted: Binding<Bool>?, perform action: @escaping ([NSItemProvider]) -> Bool) -> some View {
        self.modifier(Droppable(condition: condition, types: supportedTypes, tracking: isTargeted, action: action))
    }
}

I also wrote a version for UTType, which I prefer, because it is strongly-typed.

It enables to use the autocompleting UTType .fileURL instead of String "public.file-url"

import UniformTypeIdentifiers.UTType

fileprivate struct Droppable: ViewModifier {
    let condition: Bool
    let types: [UTType]
    var conditionalTypes: [UTType]  { condition ? types : []  }
    let tracking: Binding<Bool>?
    let action: ([NSItemProvider]) -> Bool
    
    @ViewBuilder
    func body(content: Content) -> some View {
        content.onDrop(of: conditionalTypes, isTargeted: tracking, perform: action)
    }
}

extension View {
    public func onDrop(if condition: Bool, of supportedTypes: [UTType], isTargeted: Binding<Bool>?, perform action: @escaping ([NSItemProvider]) -> Bool) -> some View {
        self.modifier(Droppable(condition: condition, types: supportedTypes, tracking: isTargeted, action: action))
    }
}
soundflix
  • 928
  • 9
  • 22