You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI 实现拖拽功能时doDrop方法未触发问题求助

SwiftUI 拖拽功能问题修复方案

1. performDrop 不触发的修复

你自定义的DropDelegate仅实现了performDrop方法,缺少必填的放置校验逻辑,SwiftUI会默认判定放置操作不合法,因此不会走到最终的放置回调。需要在doDrop结构体中补充validateDrop方法,明确告知系统当前位置接受拖拽内容。
补充后的基础doDrop结构如下:

struct doDrop: DropDelegate {
    // 新增校验方法:告知系统当前放置合法
    func validateDrop(info: DropInfo) -> DropProposal? {
        guard info.hasItemsConforming(to: ["public.text"]) else {
            return nil
        }
        return DropProposal(operation: .move)
    }
    
    func performDrop(info: DropInfo) -> Bool {
        guard info.hasItemsConforming(to: ["public.text"]) else {
            return false
        }
        if let item = info.itemProviders(for: ["public.text"]).first {
            item.loadItem(forTypeIdentifier: "public.text", options: nil) { data, error in
                DispatchQueue.main.async {
                    if let str = data as? NSString as? String {
                        print("拖拽内容:\(str)")
                    }
                }
            }
            return true
        }
        return false
    }
}

2. 获取放置目标信息的实现

如果需要拿到当前放置的目标位置信息,需要给DropDelegate增加传参,把目标对应的标识(比如条目索引、容器ID等)在初始化时传入即可。
以你当前的列表场景为例,如果要实现将某个条目拖拽到另一个条目上的逻辑,需要把onDrop修饰符挂载到每个列表元素上,同时传入当前元素的索引,完整可运行代码如下:

import SwiftUI

struct ContentView: View {
    @State var stuff = ["ABC", "DEF","GHI","JKL","MNO"]
    
    var body: some View {
        List {
            ForEach(Array(stuff.enumerated()), id: \.element) { index, x in
                Text(x)
                    .onDrag {
                        NSItemProvider(object: x as NSString)
                    }
                    // 把当前条目的索引传给DropDelegate
                    .onDrop(of: ["public.text"], delegate: doDrop(targetIndex: index, stuff: $stuff))
            }
        }
    }
    
    struct doDrop: DropDelegate {
        // 存储放置目标的索引,以及绑定的数据源
        let targetIndex: Int
        @Binding var stuff: [String]
        
        func validateDrop(info: DropInfo) -> DropProposal? {
            guard info.hasItemsConforming(to: ["public.text"]) else {
                return nil
            }
            return DropProposal(operation: .move)
        }
        
        func performDrop(info: DropInfo) -> Bool {
            guard info.hasItemsConforming(to: ["public.text"]) else {
                return false
            }
            if let item = info.itemProviders(for: ["public.text"]).first {
                item.loadItem(forTypeIdentifier: "public.text", options: nil) { data, error in
                    DispatchQueue.main.async {
                        guard let dragStr = data as? NSString as? String,
                              let dragIndex = stuff.firstIndex(of: dragStr),
                              dragIndex != targetIndex else { return }
                        // 此处已经拿到拖拽源内容、源索引、目标索引,可以自定义排序逻辑
                        stuff.move(fromOffsets: IndexSet(integer: dragIndex), toOffset: targetIndex > dragIndex ? targetIndex + 1 : targetIndex)
                        print("将\(dragStr)移动到索引\(targetIndex)位置")
                    }
                }
                return true
            }
            return false
        }
    }
}

如果是容器和条目层级的场景,只需要把传入的索引替换为容器ID、条目类型等你需要的标识即可。

内容的提问来源于stack exchange,提问作者Russ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.10 02:48:01