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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 17:36:05