You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 23:54:02