4.3 在列表、栈、网格和自定义布局中重新排序项目
5 分钟阅读
4.3 在列表、栈、网格和自定义布局中重新排序项目
使用重新排序修饰符为 SwiftUI 布局添加拖拽重新排序的交互。
概述
当你有一组或多组有序数据项目,且每个项目都在容器(例如列表、栈、网格或自定义布局)中显示为一个视图时,SwiftUI 会提供重新排序功能。系统允许用户选中某个表示数据项目的视图,并在容器中拖动它。当他们拖动该视图时,其他视图会移动,以显示一个可以放置该视图的占位空间。
当用户把视图放到占位位置上时,系统会提供与移动中的视图对应的数据项目标识符,以及这些视图的新位置。随后,你就可以在集合中移动与这些移动视图对应的数据项目,即使数组中同时发生了其他变化(例如云端同步)也没问题。
如果你把视图组织成由多个数据集合表示的区段——例如一个让用户把照片整理到相簿中的摄影应用——你可以配置重新排序,使其既支持在某个相簿内部移动照片,也支持把照片从一个相簿移动到另一个相簿。
在单个集合中重新排序项目
当你的视图由单个有序数据项目集合构建(例如摄影应用中的照片数组)时,首先为数据项目配置一个遵循 Hashable 和 Sendable 的标识符。例如,用一个遵循 Sendable 的标识符数据类型,让数据项目遵循 Identifiable。
接着,为生成这些视图的 ForEach 声明添加 reorderable() 修饰符,以指明你希望用户重新排序哪些视图,如下面代码所示:
| |
最后,为外层列表、栈、网格或自定义布局容器添加 reorderContainer(for:isEnabled:move:) 修饰符,定义界面中允许用户重新排序的区域,从而指定参与重新排序的视图范围。在 move 闭包中,指明如何应用系统提供的、用于描述你的项目集合变化情况的 ReorderDifference,如下面这个代码示例所示:
| |
跨多个集合重新排序项目
当你把视图组织成由多个数据集合构建的区段——例如摄影应用中各自包含照片的相簿——请为生成待排序视图的 ForEach 添加 reorderable(collectionID:)。然后,为包含这些视图对应数据项目的每个集合提供一个标识符,如下面这个示例所示:
| |
接着,为包含所有待排序区段和视图的列表、栈、网格或自定义布局添加 reorderContainer(for:in:isEnabled:move:),以定义重新排序区域的范围:
| |
当用户在某个相簿内部移动视图,或把视图从一个相簿移动到另一个相簿时,系统会提供一个 ReorderDifference 实例,其中包含集合标识符和位置,你可以用它们来移动该视图对应的数据项目。
支持拖放到容器之外
为容器添加重新排序修饰符只支持在容器内部移动视图。要支持在容器与应用的其他部分或其他应用之间拖放,请使用拖放修饰符。
添加 dragContainer(for:in:_:) 修饰符,为有人把视图拖到容器之外时提供传输表示。例如,下面的代码展示了如何选中一个 Photo 实例,供系统在有人把照片拖到另一个窗口或应用时获取传输表示:
| |
在 reorderContainer(for:isEnabled:move:) 修饰符之后添加 dropDestination(for:isEnabled:action:) 修饰符,以处理来自应用其他部分或其他应用的放置操作。当放置目标修饰符包住重新排序容器修饰符时,容器负责处理容器内部的拖放重新排序操作,而放置目标负责处理用户在容器之外发起的拖放操作。
在放置动作闭包内部,调用 reorderDestination(for:in:) 来找出用户把视图放到了相对于现有视图的什么位置。在下面的示例中,当该方法返回 ReorderDifference.Destination.Position.before(_:) 时,表示有人把一个或多个 Photo 表示放到了由 index 处的 Photo 实例所表示的视图之前。在这种情况下,你把被放置的项目插入到 index 处的项目之前。当该方法返回 ReorderDifference.Destination.Position.end 时,表示有人把一个或多个 Photo 表示放到了最后一个视图之后,因此你把被放置的项目追加到集合末尾。下面的代码演示了这两种情况:
| |
支持拖放到可重排容器中的单个项目上
为某个项目的视图添加 dropDestination(for:isEnabled:action:) 修饰符,让大家可以从其他应用直接把项目放到你容器中的某个特定项目上。下面的代码展示了如何为 ForEach 中的 ItemView 添加放置目标,而该 ForEach 同时已配置为支持重新排序:
| |
使用 dropDestination(for:isEnabled:action:) 修饰符的 isEnabled 参数来限制该放置目标接受哪些项目。前面的代码示例展示了如何只在某个项目的 isFolder 属性为 true 时才接受放置。
在 dropDestination(for:isEnabled:action:) 修饰符的 action 闭包中添加代码,用被放置的项目更新你的模型,因为这与你在 reorderContainer(for:isEnabled:move:) 的闭包中处理重新排序时的模型更新方式可能不同。当你的放置目标接受来自重新排序容器的项目时,请务必在把这些项目移动到该项目的数据集合时,从来源数据集合中移除它们。
动态启用和停用重新排序
你可能希望通过网络同步更新时,或者在对视图进行其他会让重新排序变复杂的更新时停用重新排序。为此,请使用带 isEnabled 参数的重新排序容器修饰符变体,并向该参数传入一个布尔属性来跟踪是否启用了重新排序,如下面这个示例所示:
| |
在这个示例中,当 isSaving 为 true 时会停用重新排序。当模型完成保存并把 isSaving 改为 false 时,重新排序便会启用。