You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI:窗口调整大小时为LazyVGrid布局变化添加动画

macOS下LazyVGrid布局调整的动画实现方案

问题描述

我在macOS应用里用了LazyVGrid组件,调整窗口宽度时它会自动适配布局、重新排列网格项。我想给这种布局变化加动画效果。我知道现在推荐用withAnimation和.animation(Animation?, value: Equatable),但网格布局是基于容器自动调整的,不依赖我自己管理的变量。原来适配这种场景的.animation(Animation?)方法已经被弃用了,但目前还能正常运行。我需要维护一个windowWidth状态变量(通过窗口resize事件更新)来实现动画吗?有没有更优的方案?

原代码示例

import SwiftUI

@main
struct MacosPlaygroundApp: App {
    let colors: [Color] = [.red, .pink, .blue, .brown, .cyan, .gray, .green, .indigo, .orange, .purple]
    let gridItem = GridItem(
        .adaptive(minimum: 100, maximum: 100),
        spacing: 10,
        alignment: .center
    )

    var body: some Scene {
        WindowGroup {
            ScrollView(.vertical, showsIndicators: false) {
                LazyVGrid(columns: [gridItem], alignment: .center, spacing: 10) {
                    ForEach(colors, id: \.self) { color in
                        color
                            .contentShape(Rectangle())
                            .cornerRadius(4)
                            .frame(width: 100, height: 100)
                    }
                }.animation(.easeIn)
            }
            .frame(minWidth: 120, maxWidth: .infinity, minHeight: 120, maxHeight: .infinity)
        }
    }
}

最优解决方案

不需要手动监听窗口resize事件,利用GeometryReader监听LazyVGrid所在容器的宽度,将其作为.animation的value参数,即可让布局变化触发合规的动画效果,完全符合SwiftUI现代动画规范。

修改后的代码示例

import SwiftUI

@main
struct MacosPlaygroundApp: App {
    let colors: [Color] = [.red, .pink, .blue, .brown, .cyan, .gray, .green, .indigo, .orange, .purple]
    let gridItem = GridItem(
        .adaptive(minimum: 100, maximum: 100),
        spacing: 10,
        alignment: .center
    )

    var body: some Scene {
        WindowGroup {
            ScrollView(.vertical, showsIndicators: false) {
                GeometryReader { geometry in
                    LazyVGrid(columns: [gridItem], alignment: .center, spacing: 10) {
                        ForEach(colors, id: \.self) { color in
                            color
                                .contentShape(Rectangle())
                                .cornerRadius(4)
                                .frame(width: 100, height: 100)
                        }
                    }
                    // 绑定容器宽度作为动画触发值,仅当宽度变化时触发动画
                    .animation(.easeIn, value: geometry.size.width)
                }
            }
            .frame(minWidth: 120, maxWidth: .infinity, minHeight: 120, maxHeight: .infinity)
        }
    }
}

方案优势

  • 无需手动处理窗口resize事件,GeometryReader会自动感知容器尺寸变化
  • 使用带value参数的.animationAPI,完全符合SwiftUI最新规范,规避弃用API的兼容性风险
  • 动画触发精准,仅在容器宽度变化时触发布局动画,避免不必要的UI异动

不推荐使用弃用API的原因

虽然.animation(Animation?)目前仍能运行,但它已被标记为弃用,未来SwiftUI版本可能移除该API,导致代码失效。此外,不带value的动画可能在无关状态变化时触发,引发意外的UI行为。

内容的提问来源于stack exchange,提问作者bzyr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 03:45:39