SwiftUI onDrag无结束反馈及NSItemProvider内存泄漏问题求助
SwiftUI
onDrag拖拽结束无反馈与NSItemProvider泄漏问题 问题描述
在使用SwiftUI的onDrag修饰符时,遇到拖拽操作结束无反馈的问题。尝试通过NSItemProvider的deinit方法添加结束逻辑,大部分场景有效,但存在特殊场景:长按触发拖拽后不拖动直接释放,NSItemProvider实例会发生泄漏,deinit方法不执行,导致拖拽状态无法重置。
复现代码如下:
import SwiftUI struct DragDropDebugView: View { @State private var isDragging = false var body: some View { VStack { Text(isDragging ? "Dragging" : "Not dragging") Color.red .onDrag( { isDragging = true return DraggableItemProvider { self.isDragging = false } }, preview: { Color.blue.frame(width: 50, height: 50) } ) } } } private final class DraggableItemProvider: NSItemProvider { private var deinitAction: () -> Void init(deinitAction: @escaping () -> Void) { print("DraggableItemProvider init") self.deinitAction = deinitAction super.init() } deinit { print("DraggableItemProvider deinit") deinitAction() } }
问题分析
这种泄漏是因为当长按后不拖动直接释放时,UIKit内部会保留NSItemProvider的引用但未正确释放,导致deinit无法触发,进而无法执行状态重置逻辑。
解决方法
放弃依赖NSItemProvider的deinit来监听拖拽结束,改用手势组合手动管控拖拽状态,同时保留onDrag的核心拖拽功能:
import SwiftUI struct DragDropFixView: View { @State private var isDragging = false var body: some View { VStack { Text(isDragging ? "Dragging" : "Not dragging") Color.red // 监听长按开始与释放(未拖动场景) .onLongPressGesture(minimumDuration: 0.5) { isDragging = true } onPressingChanged: { isPressing in if !isPressing { isDragging = false } } // 原生拖拽功能 .onDrag { NSItemProvider() } preview: { Color.blue.frame(width: 50, height: 50) } // 监听拖动结束场景 .simultaneousGesture( DragGesture() .onEnded { _ in isDragging = false } ) } } }
方案说明
- 用
onLongPressGesture的onPressingChanged回调,处理长按后不拖动直接释放的状态重置。 - 用
DragGesture的onEnded回调,处理拖动后释放的状态重置。 onDrag仅负责提供拖拽所需的NSItemProvider,不再依赖其生命周期管理状态。
这种方式覆盖了所有拖拽场景,彻底避免了NSItemProvider泄漏导致的状态异常问题。
内容的提问来源于stack exchange,提问作者Gus Campos
相关产品推荐
相关产品推荐

