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

ScrollView内嵌LazyVStack中可变高度视图动画异常问题

ScrollView内嵌LazyVStack时高度变化动画抖动的解决办法

问题背景

在Xcode 15.0、目标系统iOS 15的SwiftUI环境中,当ScrollView嵌套的LazyVStack包含高度动态变化的视图时,切换视图高度的动画会出现异常抖动。尤其当滚动到可变高度视图完全脱离可见区域后触发动画,抖动现象会更加明显。

复现代码

@State private var isOn = false

var body: some View {
    VStack {
        Toggle(isOn.description, isOn: $isOn.animation())
        ScrollView {
            LazyVStack {
                Rectangle()
                    .frame(height: isOn ? 200 : 50)
                ForEach(0..<100) { index in
                    AsyncImage(url: URL(string: "https://picsum.photos/200/300")!)
                }
            }
        }
    }
}

现象差异

  • 使用LazyVStack时,高度切换动画伴随明显抖动;
  • 替换为VStack后,动画运行流畅无卡顿。

问题原因

LazyVStack的核心是懒加载机制,仅渲染当前可见区域内的视图。当可变高度视图滚动出可见范围后,会被从视图树中移除。此时触发高度变化动画,LazyVStack需要重新计算布局并重新加载该视图,这个过程与动画的帧更新逻辑冲突,导致布局计算紊乱,最终出现抖动。而VStack会一次性渲染所有子视图,即使视图不在可见区域也会保留在视图树中,动画过程中布局计算更稳定。

解决方案

方案1:为可变高度视图添加固定标识

通过给可变高度视图设置id,强制LazyVStack在动画过程中保持该视图的布局上下文,避免因懒加载卸载视图导致的布局异常:

ScrollView {
    LazyVStack {
        Rectangle()
            .frame(height: isOn ? 200 : 50)
            .id(isOn) // 随状态变化更新id,确保视图布局上下文稳定
        ForEach(0..<100) { index in
            AsyncImage(url: URL(string: "https://picsum.photos/200/300")!)
        }
    }
}

方案2:用ScrollViewReader控制滚动位置

动画触发时,通过ScrollViewReader将滚动位置临时调整到可变高度视图附近,确保动画过程中视图处于可见状态,规避懒加载卸载逻辑:

@State private var isOn = false
@Namespace private var animationNamespace

var body: some View {
    VStack {
        Toggle(isOn.description, isOn: $isOn.animation())
        ScrollViewReader { proxy in
            ScrollView {
                LazyVStack {
                    Rectangle()
                        .frame(height: isOn ? 200 : 50)
                        .id("heightAdjustView")
                    ForEach(0..<100) { index in
                        AsyncImage(url: URL(string: "https://picsum.photos/200/300")!)
                    }
                }
            }
            .onChange(of: isOn) { _ in
                withAnimation {
                    proxy.scrollTo("heightAdjustView", anchor: .top)
                }
            }
        }
    }
}

方案3:替换为VStack(简单直接)

如果列表项数量不大,直接用VStack替代LazyVStack是最省心的方案。虽然会失去懒加载的性能优势,但能直接保证动画流畅性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 16:33:16