微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

来自 ObservedObject 的数据未在 SwiftUI 视图中呈现

如何解决来自 ObservedObject 的数据未在 SwiftUI 视图中呈现

我使用 combine 连接到 REST API,该 API 将一些数据拉入 ObservableObject。整个设置实际上只是 MVVM。 ObservableObject 然后在视图中被观察。现在,我遇到了一个似乎无法解决错误。似乎发生的是 View 被绘制了两次。第一次使用 Observedobject 中的值进行更新。然后它立即重新绘制,但现在拉出的数据突然消失了。我已经确认没有第二次执行拉取数据的代码。此外,View Hierarchy Viewer 似乎暗示应该在视图中渲染数据的文本视图在第二次渲染运行时以某种方式被删除

这是模型的样子(与身份验证相关的代码来自 Firebase):

class Itemviewmodel: ObservableObject {
    
    @Published var items = [ScheduleItem]()
    
    private var publisher: AnyCancellable?
    
    func fetchData() {
        
        let currentUser = Auth.auth().currentUser
        currentUser?.getIDTokenForcingRefresh(false,completion: { idToken,error in
        
            let url = URL(string: "abc")!
            var request = URLRequest(url: url)
            request.httpMethod = "GET"
            request.setValue("Bearer "+String(idToken!),forHTTPHeaderField: "Authorization")
     
            self.publisher = URLSession.shared
                .dataTaskPublisher(for: url)
                .map(\.data)
                .decode(
                    type: [ScheduleItem].self,decoder: JSONDecoder()
                )
                .receive(on: dispatchQueue.main)
                .sink(
                    receiveCompletion: { completion in
                        switch completion {
                        case .failure(let error):
                            print("SINKERROR")
                            print(error)
                        case .finished:
                            print("SINKSUCCESS")
                        }
                    },receiveValue: { repo in
                        print("DONE")
                        self.items.append(contentsOf: repo)
                    }
                )
        })
    }
}

打印语句的输出是 完毕 汇成 两者都只能在调试输出中找到一次。

这是视图:

struct TreatmentCardView: View {
    
    let startDate: String
    let uid: Int
    
    @Observedobject var data = Itemviewmodel()
    
    @State private var lastTime: String = "2"
    
    var body: some View {
        ZStack {
            GeometryReader { geometry in
                vstack{
                    
                    Text("Headline")
                        .font(.title)
                        .foregroundColor(Color.white)
                        .padding([.top,.leading,.bottom])
                    
                    Print("ISARRAYEMPTY")
                    Print(String(data.items.isEmpty))
                    
                    Text(String(data.items.isEmpty))
                                .font(.headline)
                                .fontWeight(.light)
                                .foregroundColor(Color.white)
                                .frame(width: 300,height: 25,alignment: .leading)
                                .multilineTextAlignment(.leading)
                                .padding(.top)
                      
                     
                }
            }
        }
        .background(Color("ColorPrimary"))
        .cornerRadius(15)
        .onAppear{
            self.data.fetchData()
        }
    }
}

Print(String(data.items.isEmpty)) 先为假,后为真,表示视图被重新渲染。

对我来说有点奇怪的是,我本来希望视图在拉取数据之前至少呈现一次,但我没有看到任何发生这种情况的迹象。

这两天我一直在努力创造这个词。非常感谢任何帮助和建议!

解决方法

我的猜测是,在您的视图层次结构中,您有一些导致 ItemViewModel 刷新的东西。由于看起来您正在使用 Firebase,我怀疑它是您的 Firebase 堆栈中的其他内容(例如用户身份验证状态?)。

我取出您的 API 请求并用一个简单的 Future 替换它,该 @StateObject 保证响应只是为了证明您的其余代码有效(它确实有效)。

您可能的解决方案是:

  1. 按照其他回答者的建议使用 ObservableObject
  2. 将您的 API 调用移动到存储在视图层次结构中较高位置的 EnvironmentObject(例如,和 TreatmentCardView),该 class ItemViewModel: ObservableObject { @Published var items = [String]() private var publisher: AnyCancellable? func fetchData() { let myFuture = Future<String,Error> { promise in DispatchQueue.main.asyncAfter(deadline: .now() + 5) { promise(.success("TEST STRING")) } } self.publisher = myFuture .receive(on: DispatchQueue.main) .sink( receiveCompletion: { completion in switch completion { case .failure(let error): print("SINKERROR") print(error) case .finished: print("SINKSUCCESS") } },receiveValue: { repo in print("DONE") self.items.append("String") } ) } } struct TreatmentCardView: View { let startDate: String let uid: Int @ObservedObject var data = ItemViewModel() @State private var lastTime: String = "2" var body: some View { ZStack { GeometryReader { geometry in VStack{ Text("Headline") .font(.title) .foregroundColor(Color.white) .padding([.top,.leading,.bottom]) // // Print("ISARRAYEMPTY") // Print(String(data.items.isEmpty)) Text(String(data.items.isEmpty)) .font(.headline) .fontWeight(.light) .foregroundColor(Color.white) .frame(width: 300,height: 25,alignment: .leading) .multilineTextAlignment(.leading) .padding(.top) } } } .background(Color("ColorPrimary")) .cornerRadius(15) .onAppear{ self.data.fetchData() } } } 被传递到 response.setContentType("application/vnd.ms-excel"); out.write(du.runReportsExcel()); 而不是每次都在那里创建它。

要查看您是否只是从 Firebase API 调用中获得另一个副作用,这里是我没有 Firebase 的简化版本:

char u_recherche;  
std::string mot_secret(choisi_ton_mot.length(),'-');  
std::string choisi_ton_mot;  
int faute = 0;

 while (i < 999)                                       
    {
        //code

        for (int i = 0; i < mot_secret.length(); i++) // to check each letter
        {
            if (choisi_ton_mot[i] == u_recherche) //guess right
            {
                mot_secret[i] = u_recherche; // if right change "-" to the right letter
                std::cout << "lettre trouver ! " << std::endl;
            }
        }
        if (choisi_ton_mot[i] != u_recherche) //guess wrong
        {
            std::cout << "rater !" << std::endl;
            faute++;
        }
,

我不知道为什么视图会自行重绘,但您可以使用 @StateObject 而不是 @ObservedObject 作为您的 ItemViewModel。您提取的数据应该保持这种状态。

@StateObject var data = ItemViewModel()

每次丢弃和重绘视图时都会重新创建 ObservedObject,其中 StateObject 保留它。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。