M1 iPad Pro上LazyVGrid无限滚动顶部项消失及崩溃问题求助
SwiftUI LazyVGrid无限滚动在M1 iPad Pro上的崩溃问题分析
编辑说明:该问题在iOS 17.2后似乎已消失。
问题现象
使用SwiftUI的LazyVGrid结合ScrollView实现无限滚动视图时,当滚动到1500+项的深度后,点击回到顶部,列表前几项会消失;此时尝试向上滚动,应用直接崩溃。该问题仅在M1 iPad Pro物理设备上出现,iPhone 15 Pro或Xcode模拟器中无此现象。
最简复现代码
ContentView.swift
import SwiftUI struct ContentView: View { @State var titles = [String]() let columns = [ GridItem(.adaptive(minimum: 160), spacing: 20, alignment: .top) ] var body: some View { ScrollView { LazyVGrid(columns: columns) { ForEach(titles, id: \.self) { title in ChildView(title: title) .onAppear { if title == titles.last { let randomInt = Int.random(in: 0..<100) let result = (titles.count...titles.count+randomInt) .map { value in String(value) } titles.append(contentsOf: result) } } } } } .onAppear { let randomInt = Int.random(in: 0..<100) let result = (titles.count...titles.count+randomInt) .map { value in String(value) } titles.append(contentsOf: result) } } }
ChildView.swift
import SwiftUI struct ChildView: View { let title: String var body: some View { Text(title) .frame(height: 100) } }
可能原因分析
- SwiftUI框架版本bug:iOS 17.2已修复该问题,说明是Apple在SwiftUI懒加载视图渲染、内存管理逻辑上的漏洞,且仅在M1 iPad Pro的硬件/渲染环境下触发边界条件。
- 视图标识稳定性不足:使用
id: \.self以字符串作为视图唯一标识,虽然当前逻辑下字符串不会重复,但大量数据下SwiftUI的哈希计算或视图复用机制可能出现异常,导致回滚时无法正确重建已回收的视图。 - 状态更新时机冲突:快速滚动回顶部时,多个视图的
onAppear可能同时触发,短时间内频繁修改titles数组,引发视图状态与数据源的不一致,进而导致渲染崩溃。 - M1设备特定渲染逻辑:M1芯片的多线程渲染、内存分页机制与SwiftUI的懒加载逻辑交互时出现异常,模拟器和iPhone的硬件环境未触发该边界条件。
修复方案
临时兼容方案(iOS 17.2以下)
- 使用稳定的视图标识:将数据源改为带唯一ID的结构体,避免依赖字符串哈希作为标识:
struct GridItemModel: Identifiable { let id = UUID() let title: String } // 在ContentView中修改状态 @State var items = [GridItemModel]() // LazyVGrid中的ForEach ForEach(items) { item in ChildView(title: item.title) .onAppear { if item.id == items.last?.id { // 加载逻辑不变,改为生成GridItemModel let randomInt = Int.random(in: 0..<100) let newItems = (items.count...items.count+randomInt) .map { GridItemModel(title: String($0)) } items.append(contentsOf: newItems) } } } - 优化加载触发逻辑:添加防抖或用
Task异步更新状态,避免同步修改导致的状态混乱:.onAppear { guard item.id == items.last?.id else { return } Task { @MainActor in // 添加短暂延迟避免频繁触发 try? await Task.sleep(nanoseconds: 200_000_000) let randomInt = Int.random(in: 0..<100) let newItems = (items.count...items.count+randomInt) .map { GridItemModel(title: String($0)) } items.append(contentsOf: newItems) } } - 使用iOS17+滚动API:用
ScrollViewReader和scrollPosition控制滚动,提升滚动稳定性:@State private var scrollID: GridItemModel.ID? var body: some View { ScrollViewReader { proxy in ScrollView { LazyVGrid(columns: columns) { // ... 视图内容 } .scrollTargetLayout() } .scrollPosition(id: $scrollID) // 回到顶部时调用 proxy.scrollTo(items.first?.id) } }
长期方案
直接升级设备系统到iOS 17.2及以上版本,Apple已在该版本中修复了此问题。
内容的提问来源于stack exchange,提问作者Doraemoe
相关产品推荐
相关产品推荐

