SwiftUI如何在用户滚动视图时暂停全视图刷新,停止滚动后自动恢复
解决方案
核心问题原因
- 当前代码直接通过
onReceive监听数据变化,一旦数据更新就立刻赋值给视图状态触发全量刷新,滚动期间触发更新就会导致UI阻塞 - 现有偏移监听仅做日志输出,没有用于判断滚动状态
ForEach用索引作为唯一标识,只要数组变动就会触发所有列表项重绘,额外加大性能损耗DataClass中@Published属性重复手动触发变更通知,会导致多次不必要的视图刷新请求
具体实现步骤
1. 新增滚动状态相关变量
在Container1中添加如下状态属性:
// 标记当前是否处于滚动状态 @State private var isScrolling = false // 滚动防抖计时器,判断滚动是否停止 @State private var scrollDebounceTimer: Timer? // 暂存滚动期间收到的最新数据,滚动停止后再更新到视图 @State private var pendingDateUpdate: String?
2. 新增滚动状态判断逻辑
修改偏移监听的onPreferenceChange代码,通过防抖逻辑判断滚动起止状态:
.onPreferenceChange(ViewOffsetKey.self) { _ in isScrolling = true // 每次滚动偏移变化时重置计时器,200ms无滚动判定为滚动停止 scrollDebounceTimer?.invalidate() scrollDebounceTimer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false) { _ in isScrolling = false // 滚动停止后应用暂存的更新 if let pending = pendingDateUpdate { myDateForDay = pending pendingDateUpdate = nil } } }
3. 修改数据监听逻辑,滚动期间暂存更新
将原有onReceive代码修改为如下逻辑,滚动期间不直接刷新视图:
.onReceive(self.data11.$myDateByDate) { response in print("response = ",response) if isScrolling { // 滚动中暂存最新数据,不触发视图刷新 pendingDateUpdate = response } else { // 未滚动状态直接更新视图 myDateForDay = response } }
4. 优化列表渲染性能
将原有基于索引遍历的ForEach改为基于元素本身遍历,避免不必要的列表项重绘:
ForEach(items, id: \.self) { item in Text("Item \(item)") }
如果后续列表元素会动态修改,建议给元素添加唯一标识并遵循Identifiable协议,进一步提升渲染效率。
5. 优化DataClass的发布逻辑
删除@Published属性多余的手动触发代码,避免重复的变更通知:
class DataClass : NSObject, ObservableObject, UNUserNotificationCenterDelegate { // 移除多余的didSet和自定义PassthroughSubject,@Published已自动实现发布能力 @Published var myDateByDate = "" override init() { self.myDateByDate = "Starting Data" } func updateValue1(){ DispatchQueue.main.asyncAfter(deadline: .now()+5.0) { self.myDateByDate = "Update Date Value" self.updateValue12() } } func updateValue12(){ DispatchQueue.main.asyncAfter(deadline: .now()+5.0) { self.myDateByDate = "Update Date Value222" self.updateValue1() } } static let sharedInstance: DataClass = { let instance = DataClass() return instance }() }
内容的提问来源于stack exchange,提问作者Sham Dhiman
相关产品推荐
相关产品推荐

