List|使用 .onMove 添加拖拽排序功能
学习如何在 SwiftUI 中使用 .onMove 修饰器实现 List 列表项的拖拽重排功能。

.onMove
是 SwiftUI 在 iOS 13 引入的修饰器,专门用于 List 和 ForEach 中的项目重排。
它是 UIKit 中 UITableView 的 .moveRowAt
方法的 SwiftUI 版本。
示例项目
苹果提供了示例项目,演示了如何使用 .onMove、.onDelete 以及 iOS 16 上新增的 .draggable。
Adopting drag and drop using SwiftUI | Apple Developer Documentation
Enable drag-and-drop interactions in lists, tables and custom views.

优势与限制
使用 .onMove
修饰符实现原生的 List 列表项重排:
- 只需要一行代码即可实现
- 自动处理 UI 反馈(拖动手柄、动画等)
注意事项
- 在 iOS 上,必须配合
EditButton()
提供编辑模式,才能显示拖动手柄。 - 专门用于列表内部重排,只能在同一个 List/ForEach 内移动。
平台差异
- iOS:需要进入编辑模式才能拖动,需要使用 EditButton。
- macOS:可以直接拖动。
用法
在 ForEach 上使用
ForEach(items) { item in
// 视图内容
}
.onMove { fromOffsets, toOffset in
items.move(fromOffsets: fromOffsets, toOffset: toOffset)
}
fromOffsets: IndexSet
- 被移动项目的原始索引集合toOffset: Int
- 目标位置的索引
创建 move() 方法
以下是 move
方法示例:

先通过 var pages = project.displayPages
获取数组副本。
然后,核心是使用 Swift 内置的数组移动方法 —— .move()
.move(fromOffsets: fromOffsets, toOffset: toOffset)
- fromOffsets 是要移动的页面索引集合(可能多个)
- toOffset 是目标位置
最后,更新排序值并保存到数据库。
- 遍历重新排序后的数组
- 将每个页面的 sortOrder 设置为它在数组中的新位置(0, 1, 2...)
持久化顺序值
在 SwiftData 中,可以添加 sortOrder
变量,用于存储 Page 的顺序:
var sortOrder: Int? // 排序顺序
避免使用 onTapGesture
避免在 List 的 Item 上使用 onTapGesture 修饰器,他会导致 .onMove 方法无法正常生效:

优先使用 List 的 selection 机制
将点击选择功能改为使用 List 原生的 selection 机制,来解决 onTapGesture 导致的冲突问题。
