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
相关产品推荐
相关产品推荐

