如何解决SwiftUI加载展示数据时视图抖动的问题?
解决SwiftUI视图因异步加载球队数据导致的抖动问题
你碰到的视图抖动,根源是API加载完成前,following_teams是空数组,对应的队徽HStack完全不显示;等数据加载完成后,这个HStack突然出现,挤压其他视图重新布局,再加上全局的.animation(.easeIn),就触发了整个视图的过渡动画,表现为抖动。
下面是几种有效的解决方法:
方法一:为动态视图预留固定占位空间
不管数据是否加载完成,都保留队徽区域的布局位置,避免布局突变。可以添加和实际队徽尺寸一致的占位视图:
修改Home视图中对应的队徽HStack部分:
HStack(spacing: 15) { if followingTeamsModel.following_teams.isEmpty { // 显示两个占位,和实际队徽尺寸匹配 ForEach(0..<2) { _ in RoundedRectangle(cornerRadius: 15) .fill(Color.gray.opacity(0.1)) .frame(width: 50, height: 50) .shadow(color: .black.opacity(0.08), radius: 5, x: 5, y: 5) } } else { ForEach(followingTeamsModel.following_teams, id: \.id) { items in KFImage(URL(string: "https://example.com/team_logo/\(items.logo_file ?? "")")!) .resizable() .scaledToFit() .cornerRadius(5) .padding(5) .frame(width: 50, height: 50) .background(Color.white) .shadow(color: .black.opacity(0.08), radius: 5, x: 5, y: 5) .cornerRadius(15) } } } .padding(.trailing, 10)
方法二:移除全局动画,仅给需要的元素加动画
全局的.animation(.easeIn)会让所有状态变化都触发动画,包括数据加载导致的布局变化。可以把全局动画去掉,只给队徽区域单独添加动画:
- 移除
NavigationView后的.animation(.easeIn) - 给队徽HStack添加条件动画:
HStack(spacing: 15) { // 原队徽循环渲染代码不变 } .padding(.trailing, 10) .animation(.easeIn, value: followingTeamsModel.following_teams)
这样只有当following_teams数据变化时,才会触发队徽区域的动画,不会影响整个视图布局。
方法三:提前加载数据
把数据加载的时机提前到视图初始化前,比如在ViewModel的初始化方法中就开始请求数据:
修改FollowingTeamsViewModel:
class FollowingTeamsViewModel: NSObject, ObservableObject { @Published var following_teams = [FollowingTeam]() init() { super.init() let defaults = UserDefaults.standard let key = defaults.string(forKey: "key") ?? "" fetchFollowingTeams(key: key) } // 原fetchFollowingTeams方法不变 }
然后在Home视图中初始化ViewModel:
@StateObject var followingTeamsModel = FollowingTeamsViewModel()
这样数据可能在视图渲染前就加载完成,避免布局变化。
最优方案
优先结合方法一+方法二,既预留了布局空间防止突然挤压,又控制了动画范围,能彻底解决抖动问题。
内容的提问来源于stack exchange,提问作者Mxyb
相关产品推荐
相关产品推荐

