SwiftUI List中使用ScrollViewReader滚动时崩溃问题求助
解决SwiftUI List结合ScrollViewReader滚动时的崩溃问题
问题原因
在iOS 16 + Xcode 14.0.1环境下,使用ScrollViewReader的scrollTo(_:)方法滚动到List中新添加的条目时,会触发EXC_BAD_INSTRUCTION崩溃。这是因为List更新视图的时机与scrollTo调用时机不匹配:当我们设置scrollItem并触发滚动时,List还未完成新条目的视图渲染与ID注册,导致ScrollViewProxy找不到对应的视图ID,进而引发崩溃。
修复方案
以下是几种可行的修复方式,任选其一即可:
方案1:异步延迟执行scrollTo
修改onChange的回调逻辑,通过DispatchQueue.main.async将滚动操作延迟到主队列空闲时执行,确保List已完成视图更新:
.onChange(of: scrollItem) { newValue in DispatchQueue.main.async { proxy.scrollTo(newValue) } }
方案2:在Task中异步设置scrollItem
修改addItem方法,通过@MainActor的Task异步设置scrollItem,等待List完成数据源更新后的视图渲染:
func addItem() { items.append(Item()) Task { @MainActor in scrollItem = items.last?.id } }
方案3:监听items变化而非scrollItem
直接监听数据源items的变化,当有新条目添加时,异步执行滚动操作:
List { ForEach(items) { item in Text(item.id.uuidString) .id(item.id) } } .listStyle(.inset) .onChange(of: items) { _ in guard let lastId = items.last?.id else { return } DispatchQueue.main.async { proxy.scrollTo(lastId) } }
这种方式可以去掉scrollItem这个状态变量,简化逻辑。
说明
以上方案均通过调整滚动操作的执行时机,确保ScrollViewProxy能找到已注册的视图ID,从而避免崩溃。同时保留了List原生的滑动删除功能,满足实际项目需求。
内容的提问来源于stack exchange,提问作者Groundstop
相关产品推荐
相关产品推荐

