在异步方法中使用 withAnimation

了解如何在 Swift 异步方法中,使用 withAnimation 添加更新动画,并理解为什么在某些情况下动画不生效。

在异步方法中使用 withAnimation

在同步函数中使用 withAnimation

下面这个示例代码,使用同步编程的方式,调用 updateDisplayRecords() 更新displayRecords,并使用 LazyVStack 进行展示。

struct ExpenseListView: View {
   // 定义属性

    var body: some View {
       if isLoading {
            ProgressView()
                .padding(.top, 50)
        } else {
            LazyVStack(spacing: 16) {
                ForEach(displayRecords, id: \.id) { record in
                    RecordRowListView(
                        record: record, useMinuteFormat: false
                    )
                    .transition(.blurReplace)
                }
            }
            .onChange(of: selectedCategoryFilter) {
                updateDisplayRecords()
            }
        }
    }

    // 同步函数
    private func updateDisplayRecords() {
        withAnimation(.spring) {
            displayRecords = filterRecords
        }
    }

    // 同步函数
    private func filterRecords() -> [Record] {
        records.filter { record in
            // 执行筛选计算
        }
    }
}

updateDisplayRecords() 中使用 withAnimation 为更新添加动画:

0:00
/0:05

在异步中使用 withAnimation 无法生效

如果将 updateDisplayRecords() 修改为异步函数,并通过.task()调用更新,那么 withAnimation 将不再生效:

struct ExpenseListView: View {
    // 定义属性

    var body: some View {
        if isLoading {
            ProgressView()
                .padding(.top, 50)
        } else {
            LazyVStack(spacing: 16) {
                ForEach(displayRecords, id: \.id) { record in
                    RecordRowListView(
                        record: record, useMinuteFormat: false
                    )
                    .transition(.blurReplace)
                }
            }
            .task(id: selectedCategoryFilter) {
                await updateDisplayRecords()
            }
        }
    }

    // 添加更新显示记录的异步函数
    private func updateDisplayRecords() async {
        // 先获取过滤后的数据
        let filteredRecords = await filterRecords()
        
        withAnimation(.spring) {
            displayRecords = filteredRecords
        }
    }

    // 将过滤逻辑改为异步函数
    private func filterRecords() async -> [Record] {
        records.filter { record in
            // 执行筛选计算
        }
    }
}
0:00
/0:04

原因分析