实现LazyVGrid数据更新时的从右至左切换动画
解决方案
1. 重构选项数据结构
先给每个选项添加唯一标识,避免因字符串重复导致SwiftUI的Diff算法识别混乱。在ViewModel中定义一个带唯一ID的结构体:
struct ChoiceItem: Identifiable { let id = UUID() let text: String }
然后将possibleChoices的类型修改为[ChoiceItem]:
@Published var possibleChoices: [ChoiceItem] = []
2. 更新ViewModel的选项生成逻辑
调整selectArrayOfChoices方法,生成带唯一ID的选项集合:
func selectArrayOfChoices() { var choices: [ChoiceItem] = [] // 添加当前问题的正确选项 let correctText = questionsToDisplay[currentQuestionIndex].nameFr choices.append(ChoiceItem(text: correctText)) var count = 0 while count < 3 { let randomText = questionsToDisplay.randomElement()!.nameFr // 确保随机选项不重复且不等于正确选项 if !choices.contains(where: { $0.text == randomText }) && randomText != correctText { choices.append(ChoiceItem(text: randomText)) count += 1 } } choices.shuffle() possibleChoices = choices }
3. 调整视图代码,添加整体切换动画
修改LazyVGrid的实现,给整个网格添加过渡动画,并在按钮点击时用withAnimation包裹数据更新操作:
LazyVGrid(columns: columns, spacing: 20) { ForEach(vmchoices.possibleChoices) { item in Button { print("Button taped") // 用withAnimation触发动画,同步更新数据 withAnimation(.easeInOut(duration: 0.5)) { vmchoices.updateCurrentQuestion() vmchoices.selectArrayOfChoices() } } label: { Text(item.text) .bold() .multilineTextAlignment(.center) .frame(width: 130, height: 130) .padding(.horizontal) .font(.title3) .foregroundColor(Color.accentColor) .background { Rectangle() .fill(.white) .shadow(color: .black.opacity(0.2), radius: 10) } } } } .padding(.horizontal) // 设置不对称过渡:旧网格向左移出,新网格从右侧移入 .transition(.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading))) // 绑定动画到选项数组的变化,确保触发时机正确 .animation(.easeInOut(duration: 0.5), value: vmchoices.possibleChoices)
关键说明
- 使用
Identifiable结构体替代纯字符串数组,让SwiftUI能精准追踪每个元素的生命周期,避免出现随机的单个按钮动画。 - 不对称过渡动画实现了整体网格从右到左的切换效果,配合
withAnimation指定0.5秒的动画时长。 - 移除了不必要的
DispatchQueue延迟,直接在动画闭包中执行数据更新,保证动画与数据变化同步。
内容的提问来源于stack exchange,提问作者axeeeeelt
相关产品推荐
相关产品推荐

