问题描述
我创建了一个可以按预期工作的拖放式视图修饰符,但现在我想让它接受任何对象。我可以将
我需要单例类,所以我可以将 .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
}
}