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

自定义PullToRefresh执行.sink操作时UI卡顿1秒问题求助

自定义下拉刷新触发UI卡顿的解决方法

用自定义PullToRefresh触发全屏刷新时,UI会卡顿1秒,移除updateFitnessClasses()里的.sink后恢复正常。引发问题的代码如下:

private func updateFitnessClasses() {
    if let firstCompany = userCompanies.first {
        updateFitnessClassesInteractor
            .update(companyID: firstCompany.id)
            .combineLatest(fitnessClassesRepository.fitnessClassesSections,
                           fitnessClassesRepository.calendarDayItems)
            .replaceError(with: ((), [], []))
            .sink(receiveValue: { [weak self] _, calendarSections, calendarRows in
                self?.fitnessClassesSections = calendarSections
                self?.calendarRows = calendarRows
            })
            .store(in: cancelBag)
    }
}

其中fitnessClassesRepository.fitnessClassesSections与fitnessClassesRepository.calendarDayItems为AnyPublisher<[CalendarSection], Error>,在视图.onAppear触发的方法中使用时无卡顿。UpdateFitnessClassesInteractor的update()实现:

func update(companyID: Int) -> AnyPublisher<Void, Error> {
    fitnessClassCache
        .lastUpdateTimestamp(forCompanyID: companyID)
        .prefix(1)
        .flatMap { self.apiClient.sendRequest(.fitnessClasses(timestamp: $0, companyID: companyID)) }
        .map { $0.data }
        .map { self.fitnessClassCache.updateCache(with: $0) }
        .eraseToAnyPublisher()
}

问题原因

卡顿的核心是UI更新操作没有在主线程执行。你用combineLatest合并的三个Publisher中,fitnessClassesRepository的两个Publisher是从数据库读取数据后在后台线程发送值的,而sink默认会在上游Publisher的线程执行闭包——这就导致你在闭包里更新绑定UI的fitnessClassesSections和calendarRows时,是在后台线程操作,直接引发UI卡顿。

而.onAppear里使用时不卡,大概率是因为当时你做了线程切换,或者上游Publisher本身是在主线程发送值。

解决方案

在.sink之前添加receive(on: DispatchQueue.main),强制将闭包执行切换到主线程,确保UI属性的更新在主线程完成:

private func updateFitnessClasses() {
    if let firstCompany = userCompanies.first {
        updateFitnessClassesInteractor
            .update(companyID: firstCompany.id)
            .combineLatest(fitnessClassesRepository.fitnessClassesSections,
                           fitnessClassesRepository.calendarDayItems)
            .replaceError(with: ((), [], []))
            .receive(on: DispatchQueue.main) // 新增线程切换代码
            .sink(receiveValue: { [weak self] _, calendarSections, calendarRows in
                self?.fitnessClassesSections = calendarSections
                self?.calendarRows = calendarRows
            })
            .store(in: cancelBag)
    }
}

另外可以额外检查fitnessClassCache.updateCache(with:)方法,如果这是耗时的数据库写入操作,要确保它在后台线程执行(目前你的update()方法中map操作已经在API请求的后台线程,但如果不是的话,可添加subscribe(on: DispatchQueue.global())切换线程),避免阻塞主线程。

内容的提问来源于stack exchange,提问作者Vader20FF

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 09:55:19