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

异步加载数据时iOS界面抖动(刷新轮跳转)问题修复咨询

嘿,咱们来一步步解决你的界面抖动和元素显示问题~先从代码里的异步逻辑问题入手,再调整UI的显示时机:

一、修正异步数据加载的核心问题

你的getAlertsDataDebug方法里混用了Firebase的闭包回调和async/await,这会导致SwiftUI无法准确感知数据加载的完成时机——await会直接返回,而闭包还在后台跑,界面会提前刷新,这就是闪空白和刷新轮抖动的根源之一。

把Firebase的回调改成async/await风格(Firebase 9+支持getData()方法),确保await真正等待数据加载完成:

class AlertListDebug: ObservableObject {
    @Published var alerts = [String:[String:Any]]()

    @MainActor
    func getAlertsDataDebug(refAlertsRaw: DatabaseReference!, officialTwitter: String) async {
        self.alerts = [:]
        
        let query = refAlertsRaw.child("alerts").child("raw_" + officialTwitter)
        
        do {
            // 用async/await替代闭包回调
            let snapshot = try await query.getData()
            if let dict = snapshot.value as? [String: Any] {
                for (key, alertRaw) in dict {
                    guard let line = alertRaw["Line"], let text = alertRaw["Text"] else { continue }
                    self.alerts[key] = ["lines": line, "text": text]
                }
            }
        } catch {
            print("加载告警数据失败:\(error.localizedDescription)")
        }
    }
}

二、解决刷新轮跳转与闪空白问题

1. 添加加载状态追踪

在AlertListDebug里新增一个isLoading标记,用来控制加载指示器和内容的显示时机:

class AlertListDebug: ObservableObject {
    @Published var alerts = [String:[String:Any]]()
    @Published var isLoading = false // 新增加载状态
    
    @MainActor
    func getAlertsDataDebug(refAlertsRaw: DatabaseReference!, officialTwitter: String) async {
        self.isLoading = true // 开始加载时标记为true
        self.alerts = [:]
        
        let query = refAlertsRaw.child("alerts").child("raw_" + officialTwitter)
        
        do {
            let snapshot = try await query.getData()
            if let dict = snapshot.value as? [String: Any] {
                for (key, alertRaw) in dict {
                    guard let line = alertRaw["Line"], let text = alertRaw["Text"] else { continue }
                    self.alerts[key] = ["lines": line, "text": text]
                }
            }
        } catch {
            print("加载告警数据失败:\(error.localizedDescription)")
        } finally {
            self.isLoading = false // 无论成功失败,结束加载标记为false
        }
    }
}

2. 控制UI的显示逻辑

在视图里根据isLoading和alerts.count来切换显示内容,确保加载时只显示固定位置的加载指示器,数据加载完成后再展示列表和「All Tweets」:

struct AlertsViewDebug: View {
    @StateObject var alertList = AlertListDebug()
    @State var officialTwitter = "official"
    var refAlertsRaw: DatabaseReference! = Database.database().reference()
    
    var body: some View {
        VStack(alignment: .leading, spacing: 0){
            // 顶部切换按钮区域保持不变
            HStack {
                Button(action: {
                    officialTwitter = "official";
                    Task {
                        await alertList.getAlertsDataDebug(refAlertsRaw: refAlertsRaw, officialTwitter: officialTwitter)
                    }
                }) {
                    Text("Official Alerts")
                }
                Spacer()
                Button(action: {
                    officialTwitter = "twitter";
                    Task {
                        await alertList.getAlertsDataDebug(refAlertsRaw: refAlertsRaw, officialTwitter: officialTwitter)
                    }
                }) {
                    Text("Twitter Feed")
                }
            }
            
            ScrollView {
                VStack(spacing: 0) {
                    // 加载中显示固定位置的进度条,防止跳转
                    if alertList.isLoading {
                        ProgressView()
                            .frame(maxWidth: .infinity, maxHeight: 200)
                    }
                    // 数据加载完成且有内容时显示列表
                    else if alertList.alerts.count > 0 {
                        ForEach(alertList.alerts.keys.sorted(), id: \.self) {alertKey in
                            Text(alertKey)
                                .font(.system(size: 16))
                                .foregroundColor(.black)
                                .frame(width: 320)
                        }
                        .overlay(
                            RoundedRectangle(cornerRadius: 10)
                                .stroke(.black, lineWidth: 1)
                        )
                        .padding(.vertical, 5)
                        
                        // 仅在Twitter模式且数据加载完成后显示「All Tweets」
                        if officialTwitter == "twitter" {
                            Text("All Tweets")
                                .padding(.top, 10)
                        }
                    }
                    // 无数据时显示提示
                    else {
                        Text("暂无告警数据")
                            .frame(maxWidth: .infinity, maxHeight: 200)
                            .foregroundColor(.gray)
                    }
                }
            }
            .task {
                await alertList.getAlertsDataDebug(refAlertsRaw: refAlertsRaw, officialTwitter: officialTwitter)
            }
            .refreshable {
                await alertList.getAlertsDataDebug(refAlertsRaw: refAlertsRaw, officialTwitter: officialTwitter)
            }
            
            Spacer()
        }
        .navigationTitle("Alerts")
        .navigationBarTitleDisplayMode(.inline)
    }
}

三、额外优化建议

  • 用自定义结构体替代[String:[String:Any]]存储告警数据,避免强制解包的风险,代码可读性也更高:
struct Alert: Identifiable {
    let id: String
    let lines: String
    let text: String
}

然后把alerts改成@Published var alerts = [Alert](),遍历的时候更方便。

  • 按钮点击时可以添加防抖逻辑,避免重复点击触发多次数据请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 17:15:48