如何使拖放视图修饰符接受任何对象

问题描述

我创建了一个可以按预期工作的拖放式视图修饰符,但现在我想让它接受任何对象。我可以将 添加到所有函数、结构和视图修饰符,但是当我尝试将它添加到我的单例类时,我得到“通用类型不支持静态存储属性”。

我需要单例类,所以我可以将 .dropObjectOutside viewmodifier 放在我的视图层次结构中的任何位置,所以我尝试将 ID 向下转换为字符串,但我似乎无法做到这一点。

有没有办法向下转换或使此代码接受任何对象?

import SwiftUI

// I want this to be any object
struct StopContent: Identifiable {
    var id: String = UUID().uuidString
}

// Singleton class to hold drag state
class DragToReorderController: ObservableObject {

    // Make it a singleton,so it can be accessed from any view
    static let shared = DragToReorderController()
    private init() { }

    @Published var draggedID: String? // How do I make this a T.ID or downcast T.ID to string everywhere else?
    @Published var dragActive:Bool = false

}


// Add ViewModifier to view
extension View {

    func dragToReorder(_ item: StopContent,array: Binding<[StopContent]>) -> some View {
        self.modifier(DragToReorderObject(sourceItem: item,contentArray: array))
    }

    func dropOutside() -> some View {
        self.onDrop(of: [UTType.text],delegate: DropObjectOutsideDelegate())
    }
}


import UniformTypeIdentifiers

// MARK: View Modifier
struct DragToReorderObject: ViewModifier {

    let sourceItem: StopContent
    @Binding var contentArray: [StopContent]

    @Observedobject private var dragReorder = DragToReorderController.shared

    func body(content: Content) -> some View {
        content
        .onDrag {
            dragReorder.draggedID = sourceItem.id
            dragReorder.dragActive = false
            return NSItemProvider(object: String(sourceItem.id) as Nsstring)
        }
        .onDrop(of: [UTType.text],delegate: DropObjectDelegate(sourceItem: sourceItem,listData: $contentArray,draggedItem: $dragReorder.draggedID,dragActive: $dragReorder.dragActive))
        .onChange(of: dragReorder.dragActive,perform: { value in
            if value == false {
                // Drag completed
            }
        })
        .opacity(dragReorder.draggedID == sourceItem.id && dragReorder.dragActive ? 0 : 1)
            
    }
}

// MARK: Drop and reorder
struct DropObjectDelegate: DropDelegate {

    let sourceItem: StopContent
    @Binding var listData: [StopContent]
    @Binding var draggedItem: String?
    @Binding var dragActive: Bool


    func dropEntered(info: DropInfo) {
    
        if draggedItem == nil { draggedItem = sourceItem.id }
    
        dragActive = true
    
        // Make sure the dragged item has moved and that it still exists
        if sourceItem.id != draggedItem {
            if let draggedItemValid = draggedItem {
                if let from = listData.firstIndex(where: { $0.id == draggedItemValid } ) {
                
                    // If that is true,move it to the new location
                    let to = listData.firstIndex(where: { $0.id == sourceItem.id } )!
                
                    if listData[to].id != draggedItem! {
                        listData.move(fromOffsets: IndexSet(integer: from),toOffset: to > from ? to + 1 : to)
                    }
                }
            }
        }
    }

    func dropUpdated(info: DropInfo) -> DropProposal? {
        return DropProposal(operation: .move)
    }

    func performDrop(info: DropInfo) -> Bool {
        dragActive = false
        draggedItem = nil
        return true
    }

}


// MARK: Drop and cancel
struct DropObjectOutsideDelegate: DropDelegate {

    // Using a singleton so we can drop anywhere
    @Observedobject private var dragReorder = DragToReorderController.shared
    
    func dropEntered(info: DropInfo) {
        dragReorder.dragActive = true
    }

    func performDrop(info: DropInfo) -> Bool {
        dragReorder.dragActive = false
        dragReorder.draggedID = nil
        return true
    }
}

解决方法

为此,您必须在任何地方添加 Identifiable 通用约束。此外,对 draggedID 使用 Int 而不是 String。

这是演示代码。

// Singleton class to hold drag state
class DragToReorderController: ObservableObject {

    // Make it a singleton,so it can be accessed from any view
    static let shared = DragToReorderController()
    private init() { }

    @Published var draggedID: Int?
    @Published var dragActive: Bool = false
}


// Add ViewModifier to view
extension View {

    func dragToReorder<T: Identifiable>(_ item: T,array: Binding<[T]>) -> some View {
        self.modifier(DragToReorderObject(sourceItem: item,contentArray: array))
    }

    func dropOutside() -> some View {
        self.onDrop(of: [UTType.text],delegate: DropObjectOutsideDelegate())
    }
}


import UniformTypeIdentifiers

// MARK: View Modifier
struct DragToReorderObject<T: Identifiable>: ViewModifier {
    
    let sourceItem: T
    @Binding var contentArray: [T]
    
    @ObservedObject private var dragReorder = DragToReorderController.shared
    
    func body(content: Content) -> some View {
        content
            .onDrag {
                dragReorder.draggedID = sourceItem.id.hashValue
                dragReorder.dragActive = false
                return NSItemProvider(object: String(sourceItem.id.hashValue) as NSString)
            }
            .onDrop(of: [UTType.text],delegate: DropObjectDelegate(sourceItem: sourceItem,listData: $contentArray,draggedItem: $dragReorder.draggedID,dragActive: $dragReorder.dragActive))
            .onChange(of: dragReorder.dragActive,perform: { value in
                if value == false {
                    // Drag completed
                }
            })
            .opacity((dragReorder.draggedID == sourceItem.id.hashValue) && dragReorder.dragActive ? 0 : 1)
    }
}

// MARK: Drop and reorder
struct DropObjectDelegate<T: Identifiable>: DropDelegate {

    let sourceItem: T
    @Binding var listData: [T]
    @Binding var draggedItem: Int?
    @Binding var dragActive: Bool


    func dropEntered(info: DropInfo) {
    
        if draggedItem == nil { draggedItem = sourceItem.id.hashValue }
    
        dragActive = true
    
        // Make sure the dragged item has moved and that it still exists
        if sourceItem.id.hashValue != draggedItem {
            if let draggedItemValid = draggedItem {
                if let from = listData.firstIndex(where: { $0.id.hashValue == draggedItemValid } ) {
                
                    // If that is true,move it to the new location
                    let to = listData.firstIndex(where: { $0.id == sourceItem.id } )!
                
                    if listData[to].id.hashValue != draggedItem! {
                        listData.move(fromOffsets: IndexSet(integer: from),toOffset: to > from ? to + 1 : to)
                    }
                }
            }
        }
    }

    func dropUpdated(info: DropInfo) -> DropProposal? {
        return DropProposal(operation: .move)
    }

    func performDrop(info: DropInfo) -> Bool {
        dragActive = false
        draggedItem = nil
        return true
    }

}


// MARK: Drop and cancel
struct DropObjectOutsideDelegate: DropDelegate {

    // Using a singleton so we can drop anywhere
    @ObservedObject private var dragReorder = DragToReorderController.shared
    
    func dropEntered(info: DropInfo) {
        dragReorder.dragActive = true
    }

    func performDrop(info: DropInfo) -> Bool {
        dragReorder.dragActive = false
        dragReorder.draggedID = nil
        return true
    }
}