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

如何基于Core Data实现在LazyVGrid中的拖拽排序功能?

Core Data + LazyVGrid 实现拖拽自动重排列表

你的问题核心是当前代码仅交换两个项目的order值,而原生SwiftUI列表会自动调整拖拽路径中所有中间项目的位置。以下是修改后的完整实现:

修改后的代码

import SwiftUI
import CoreData

struct TopListTest: View {
    @Environment(\.managedObjectContext) var context
    @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "order", ascending: true)])
    var array: FetchedResults<Item>

    @State private var dragging: Item?

    var body: some View {
        NavigationView {
            ScrollView {
                LazyVGrid(columns: [GridItem(.adaptive(minimum: .greatestFiniteMagnitude))]) {
                    ForEach(array) { item in
                        listItemView(item: item)
                            .opacity(dragging == item ? 0.5 : 1) // 拖拽时的视觉反馈
                            .onDrag {
                                dragging = item
                                return NSItemProvider(object: String(item.order) as NSString)
                            }
                            .onDrop(of: ["public.text"], delegate: DragRelocateDelegate(
                                item: item,
                                current: $dragging,
                                context: context,
                                allItems: Array(array)
                            ))
                    }
                }
            }
        }
    }
}

struct DragRelocateDelegate: DropDelegate {
    let item: Item
    @Binding var current: Item?
    let context: NSManagedObjectContext
    let allItems: [Item]

    func performDrop(info: DropInfo) -> Bool {
        guard let currentItem = current, currentItem != item else { return false }

        let fromOrder = currentItem.order
        let toOrder = item.order

        // 获取排序后的项目数组
        let sortedItems = allItems.sorted { $0.order < $1.order }
        guard let fromIndex = sortedItems.firstIndex(of: currentItem),
              let toIndex = sortedItems.firstIndex(of: item) else { return false }

        // 根据拖拽方向调整中间项目的order
        if fromIndex < toIndex {
            // 向下拖拽,中间项目order减1
            for index in fromIndex+1...toIndex {
                sortedItems[index].order -= 1
            }
        } else {
            // 向上拖拽,中间项目order加1
            for index in toIndex...fromIndex-1 {
                sortedItems[index].order += 1
            }
        }

        // 设置拖拽项目的最终order
        currentItem.order = toOrder

        // 保存Core Data上下文
        do {
            try context.save()
        } catch {
            print("保存失败: \(error)")
        }

        current = nil
        return true
    }

    // 拖拽进入时的临时视觉反馈(可选)
    func dropEntered(info: DropInfo) {
        guard let currentItem = current, currentItem != item else { return }
        // 这里可以添加临时位置交换的动画,进一步贴近原生体验
    }
}

struct listItemView: View {
    var item: Item

    var body: some View {
        HStack {
            Text("\(item.order)")
            Spacer()
            Text(item.name ?? "")
        }
        .padding()
        .background(Color.gray.opacity(0.1))
        .cornerRadius(8)
    }
}

关键修改点

  • 批量调整Order值:不再仅交换两个项目的order,而是根据拖拽方向(向上/向下),批量调整路径中所有中间项目的order,实现类似原生列表的自动重排效果。
  • Core Data上下文保存:修改order后必须保存上下文,确保变更持久化。
  • 视觉反馈优化:给拖拽中的项目添加透明度变化,提升交互体验。
  • 参数传递优化:在DragRelocateDelegate中传入完整的项目数组和Core Data上下文,用于计算和更新order。

注意事项

  • 确保Core Data实体Item中的order属性为Int32或Int类型,且允许修改。
  • onDrop的类型改为["public.text"],因为我们传递的是字符串类型的order值,和onDrag中的类型匹配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 08:45:39