LazyVGrid与ScrollView滚动卡顿性能问题求助
真机滚动卡顿问题排查与优化方案
问题现象
真机调试时,滚动至特定位置后回滚出现明显卡顿,动画流畅度下降;替换Section为VStack后问题仍存在,但模拟器测试无异常。
问题代码
import SwiftUI import Kingfisher struct WorksDateView: View { let columns: [GridItem] = [ GridItem(.flexible(), spacing: 8), GridItem(.flexible(), spacing: 8), GridItem(.flexible(), spacing: 8) ] var data: [DateWorksModel] var body: some View { ScrollView { LazyVGrid(columns: columns, spacing: 16) { ForEach(data) { item in Section { ForEach(item.works) { work in NavigationLink(destination: WorkDetailView(workId: work.id)) { WorksDateCell( workImageCover: work.imageCover, workId: work.id, actressName: work.actressName, actressAvatar: work.actressAvatar ) } .buttonStyle(PlainButtonStyle()) } } header: { Text(item.releaseDate) .font(.title) .fontWeight(.bold) .frame(maxWidth: .infinity, alignment: .leading) .padding(.top, 8) } } } .padding(.horizontal) } } } struct WorksDateCell: View { var workImageCover: String = "https://fakeimg.pl/320x504/fe9a2b/000" var workId: String = "xxx" var actressName: String = "xxx" var actressAvatar: String = "https://fakeimg.pl/60x60/ff425a/100" var avatarSize: CGFloat = 30.0 var body: some View { VStack(alignment: .leading) { KFImage(URL(string: workImageCover)) .placeholder { ProgressView() } .resizable() .scaledToFit() .clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous)) HStack { KFImage(URL(string: actressAvatar)) .placeholder { ProgressView() } .resizable() .scaledToFill() .frame(width: avatarSize, height: avatarSize) .clipShape(Circle()) VStack(alignment: .leading) { Text(workId) .fontWeight(.medium) .font(.subheadline) Text(actressName) .foregroundStyle(.secondary) .font(.caption) .lineLimit(1) } } } } }
优化建议
1. 图片加载优化(Kingfisher)
图片加载是真机卡顿的核心诱因,需针对性优化:
- 强制缓存原始图片,避免重复下载:给
KFImage添加cacheOriginalImage()和diskCacheExpiration(.days(7)) - 取消滑出屏幕的图片请求:添加
.cancelOnDisappear(true),释放未完成请求的资源 - 固定图片尺寸,避免动态布局计算:给封面图设置基于列宽的比例高度
// 修改后的WorksDateCell封面图部分 KFImage(URL(string: workImageCover)) .placeholder { ProgressView() } .resizable() .scaledToFit() .frame(height: (UIScreen.main.bounds.width - 32)/3 * 1.58) // 适配3列布局的固定高度 .clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous)) .cacheOriginalImage() .diskCacheExpiration(.days(7)) .cancelOnDisappear(true)
2. 布局稳定性优化
- 固定
LazyVGrid列宽,避免布局波动:将GridItem(.flexible())改为带固定最小宽度的配置
let columns: [GridItem] = [ GridItem(.flexible(minimum: (UIScreen.main.bounds.width - 32)/3 - 8), spacing: 8), GridItem(.flexible(minimum: (UIScreen.main.bounds.width - 32)/3 - 8), spacing: 8), GridItem(.flexible(minimum: (UIScreen.main.bounds.width - 32)/3 - 8), spacing: 8) ]
- 避免提前初始化目标视图:将
NavigationLink改为延迟加载模式(iOS16+可用navigationDestination)
// 修改后的WorksDateView结构 NavigationStack { ScrollView { LazyVGrid(columns: columns, spacing: 16) { ForEach(data) { item in VStack(alignment: .leading) { Text(item.releaseDate) .font(.title) .fontWeight(.bold) .frame(maxWidth: .infinity, alignment: .leading) .padding(.top, 8) ForEach(item.works) { work in NavigationLink(value: work.id) { WorksDateCell( workImageCover: work.imageCover, workId: work.id, actressName: work.actressName, actressAvatar: work.actressAvatar ) } .buttonStyle(PlainButtonStyle()) } } } } .padding(.horizontal) } .navigationDestination(for: String.self) { workId in WorkDetailView(workId: workId) } }
3. 性能排查工具
用Xcode自带工具定位根因:
- Core Animation Instrument:查看卡顿帧的耗时阶段(布局/渲染/IO)
- Debug View Hierarchy:检查视图层级是否过深,有无冗余视图
内容的提问来源于stack exchange,提问作者Windy418
相关产品推荐
相关产品推荐

