SwiftUI 适配iOS13的自定义网格如何实现元素重排动画
iOS13环境下手动搭建SwiftUI网格的位移动画实现
实际遇到的问题
- 开发约束:需适配iOS13,无法使用iOS14及以上版本才提供的
LazyVGrid组件,当前采用嵌套双层ForEach的方式,外层遍历行索引、内层遍历列索引手动堆叠网格布局 - 动画异常:调整行、列索引数组顺序可触发基础动画,但打乱元素数组触发重排时,元素会直接跳转到新位置,没有跨位置的平滑位移动画效果
- 原有测试代码如下:
struct ContentView: View { @State var elements = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] @State var columns = [0, 1] @State var rows: [Int] = [0, 1, 2, 3, 4] var body: some View { VStack { ForEach(rows, id: \.self) { i in HStack { ForEach(columns, id: \.self) { j in Text("element\(elements[2 * i + j])") } } } Button("Randomize") { self.elements.shuffle() } }.animation(.default) } }
动画失效的核心原因
原写法中,HStack、VStack负责固定排布每个网格位置,每个网格位置的容器视图身份是固定的“第i行第j列”,打乱元素数组时,容器本身的位置从来没有变过,只是容器内部的文本内容被替换。SwiftUI无法识别“同一个元素从A位置移动到B位置”的变化,只会判定为A位置的容器换了内容、B位置的容器也换了内容,因此不会触发跨位置的位移动画。
适配实现方案
- 调整视图身份绑定逻辑:不再按行、列的位置硬算元素下标,直接把每个网格元素本身作为
ForEach的遍历目标,给每个视图绑定和元素值对应的稳定身份标识,保证同一个元素不管怎么移位,对应的视图实例不会被销毁 - 重构位置计算方式:放弃靠HStack、VStack自动堆叠排布位置的逻辑,给每个元素设置统一的固定尺寸,根据元素在当前数组里的下标,计算对应的横向、纵向偏移值,用
offset修饰符控制元素的实际展示位置 - 适配iOS13的动画触发逻辑:iOS13下使用
withAnimation包裹数组修改的逻辑块,不需要把动画修饰符挂在最外层容器上,避免全局无意义的动画触发
可直接运行的修正代码
struct ContentView: View { @State private var elements = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] // 固定网格列数 let columnCount = 2 // 单个网格元素的固定尺寸 let itemSize: CGFloat = 100 var body: some View { ZStack(alignment: .topLeading) { // 以元素值为身份遍历所有视图,保证视图实例不会因数组重排被重建 ForEach(elements, id: \.self) { element in Text("element\(element)") .frame(width: itemSize, height: itemSize) .background(Color.gray.opacity(0.2)) // 根据元素在数组中的当前下标计算对应的展示位置 .offset( x: CGFloat(elements.firstIndex(of: element)! % columnCount) * itemSize, y: CGFloat(elements.firstIndex(of: element)! / columnCount) * itemSize ) } Button("Randomize") { // iOS13下通过withAnimation包裹状态修改,自动触发位置插值动画 withAnimation(.easeInOut(duration: 0.3)) { elements.shuffle() } } .padding(.top, itemSize * CGFloat(elements.count / columnCount) + 20) } } }
实现效果
元素数组打乱时,每个对应元素的视图实例保持不变,仅根据自身在数组中的新下标更新offset值,SwiftUI会自动计算旧offset到新offset之间的插值过程,实现元素在网格内平滑滑动换位的效果。整套逻辑不依赖iOS14以上的系统组件,可直接在iOS13环境运行。
内容的提问来源于stack exchange,提问作者Sorin Lica
相关产品推荐
相关产品推荐

