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

SwiftUI实现同List视图内多行拖拽排序(仿提醒APP)

实现苹果提醒APP式的多选拖拽移动功能

默认的SwiftUI List 自带的.onMove仅支持编辑模式下的单条行移动,要复刻苹果提醒APP的「先多选、再拖拽移动多行」的交互,需要结合多选状态管理和自定义拖拽/插入逻辑,以下是修改后的完整实现:

import SwiftUI
import UniformTypeIdentifiers

struct ContentView: View {
    @State private var rows = [
        "First",
        "Second",
        "Third",
        "Fourth",
        "Fifth"
    ]
    // 记录选中的行
    @State private var selectedRows = Set<String>()

    var body: some View {
        List {
            ForEach(rows) { row in
                RowCell(row: row, isSelected: selectedRows.contains(row))
                    .onTapGesture {
                        // 点击切换选中状态
                        if selectedRows.contains(row) {
                            selectedRows.remove(row)
                        } else {
                            selectedRows.insert(row)
                        }
                    }
                    .onDrag {
                        // 拖拽时携带选中的所有行(如果有选中),否则只携带当前行
                        let dragItems = selectedRows.isEmpty ? [row] : Array(selectedRows)
                        let data = try! JSONEncoder().encode(dragItems)
                        return NSItemProvider(object: data as NSData)
                    }
            }
            .onDelete(perform: deleteRows)
            .onInsert(of: [UTType.data]) { destination, items in
                // 处理拖拽插入,实现多行移动
                guard let item = items.first else { return }
                item.loadObject(ofClass: NSData.self) { data, _ in
                    guard let data = data as? Data,
                          let draggedRows = try? JSONDecoder().decode([String].self, from: data) else { return }
                    
                    DispatchQueue.main.async {
                        // 先移除选中的行
                        rows.removeAll { draggedRows.contains($0) }
                        // 计算插入位置:如果插入位置在被移除行的原位置之后,需要调整偏移
                        var insertIndex = destination
                        let originalIndices = draggedRows.compactMap { rows.firstIndex(of: $0) }
                        if let maxOriginalIndex = originalIndices.max(), destination > maxOriginalIndex {
                            insertIndex -= draggedRows.count
                        }
                        // 插入到目标位置
                        rows.insert(contentsOf: draggedRows, at: insertIndex)
                        // 保持选中状态
                        selectedRows = Set(draggedRows)
                    }
                }
            }
        }
        .animation(.default, value: selectedRows)
        .animation(.default, value: rows)
    }

    private func deleteRows(offsets: IndexSet) {
        rows.remove(atOffsets: offsets)
        // 删除后同步选中状态
        selectedRows.removeAll { row in
            !rows.contains(row)
        }
    }
}

struct RowCell: View {
    let row: String
    let isSelected: Bool

    var body: some View {
        Text(row)
            .padding(.vertical, 8)
            .background(isSelected ? Color(UIColor.systemGray5) : Color.clear)
            .cornerRadius(6)
    }
}

extension String: Identifiable {
    public var id: String { self }
}

关键修改说明

  • 多选状态管理:新增selectedRows集合,记录用户点击选中的行,点击单元格可切换选中状态
  • 拖拽数据携带:拖拽时判断是否有选中行,携带所有选中行的JSON编码数据,而非单条数据
  • 自定义插入逻辑:在.onInsert中解码拖拽数据,先移除原位置的选中行,再插入到目标位置,同时修正插入偏移(避免因移除行导致位置计算错误)
  • 状态同步:删除行后自动清除已删除行的选中状态,拖拽移动后保持选中状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 07:16:28