未在SwiftUI视图中呈现来自ObservedObject的数据
我使用combine连接到一个RESTAPI,该API将一些数据拉入一个ObservieObject。整个设置实际上只是MVVM。然后在视图中观察ObservieObject。现在,我遇到了一个无法解决的问题。似乎发生了两次这样的情况。第一次使用ObservedObject中的值进行更新时。然后它立即重新绘制,但现在提取的数据突然消失了。我已经确认提取数据的代码不会再次执行。此外,“视图层次查看器”似乎建议在第二次渲染运行时以某种方式删除应该渲染视图中数据的文本视图 这就是模型的外观(与身份验证相关的代码来自Firebase): print语句的输出是 完成 成功 两者在调试输出中只能找到一次 这是一种观点:未在SwiftUI视图中呈现来自ObservedObject的数据,swiftui,combine,observableobject,observedobject,Swiftui,Combine,Observableobject,Observedobject,我使用combine连接到一个RESTAPI,该API将一些数据拉入一个ObservieObject。整个设置实际上只是MVVM。然后在视图中观察ObservieObject。现在,我遇到了一个无法解决的问题。似乎发生了两次这样的情况。第一次使用ObservedObject中的值进行更新时。然后它立即重新绘制,但现在提取的数据突然消失了。我已经确认提取数据的代码不会再次执行。此外,“视图层次查看器”似乎建议在第二次渲染运行时以某种方式删除应该渲染视图中数据的文本视图 这就是模型的外观(与身份验证
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))首先为false,然后为true,表示视图已重新呈现
对我来说有点奇怪的是,我本来希望视图在提取数据之前至少呈现一次,但我看不到任何发生这种情况的迹象
这两天来我一直在想这个词。非常感谢您的帮助和建议 我不知道为什么视图会重新绘制自身,但是您可以对
项目视图模型使用@StateObject
而不是@ObservedObject
。你的数据应该保持这种状态
@StateObject var data = ItemViewModel()
ObservedObject
将在每次丢弃和重画视图时重新创建,其中StateObject
保留它。我不知道为什么视图会重画自己,但是您可以对项目视图模型使用@StateObject
而不是@ObservedObject
。你的数据应该保持这种状态
@StateObject var data = ItemViewModel()
ObservedObject
将在每次丢弃和重新绘制视图时重新创建,其中StateObject
保留它。我猜在视图层次结构的更高层,您有一些东西导致ItemViewModel
刷新。因为它看起来像是在使用Firebase,所以我怀疑它是Firebase堆栈中的其他东西(比如用户身份验证状态?)
我取出了您的API请求,并用一个简单的Future
替换了它,该请求保证只响应以证明您的其余代码工作正常(确实如此)
您可能的解决方案是:
按照另一位回答者的建议,使用@StateObject
将API调用移动到视图层次结构中存储在较高位置的observateObject
(例如,EnvironmentObject
),该对象将传递到TreatmentCardView
,而不是每次都在那里创建它
为了了解Firebase API调用是否带来了另一个副作用,下面是我的简化版,没有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)
}
)
})
}
}
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()
}
}
}
class ItemViewModel:ObserveObject{
@已发布的变量项=[String]()
私有var发布者:是否可以取消?
func fetchData(){
让我的未来=未来{承诺
DispatchQueue.main.asyncAfter(截止日期:.now()+5){
承诺(.success(“测试字符串”))
}
}
self.publisher=myFuture
.receive(在:DispatchQueue.main上)
.水槽(
receiveCompletion:{在中完成
交换完成{
案例。失败(let错误):
打印(“错误”)
打印(错误)
案例。完成:
打印(“成功”)
}
},
receiveValue:{repo in
打印(“完成”)
self.items.append(“字符串”)
}
)
}
}
结构处理卡片视图:视图{
让开始日期:字符串
让uid:Int
@ObservedObject var data=ItemViewModel()
@State private var lastTime:String=“2”
var body:一些观点{
ZStack{
GeometryReader{中的几何体
VStack{
正文(“标题”)
.font(.title)
.foregroundColor(颜色.白色)
.padding([.top、.leading、.bottom])
//
//打印(“ISARRAYEMPTY”)
//打印(字符串(data.items.isEmpty))
文本(字符串(data.items.isEmpty))
.font(.headline)
.fontWeight(.light)
.foregroundColor(颜色.白色)
.框架(宽度:300,高度:25,对齐:。前导)
.multilitextalignment(.leading)
.padding(.top)
}
}
}
.背景(颜色(“彩色原色”))
.转弯半径(15)
奥纳佩尔先生{
self.data.fetchData()
}
}
}
我的猜测是,在视图层次结构的更高层,您有一些东西导致ItemViewModel
刷新。因为它看起来像是在使用Firebase,所以我怀疑它是Firebase堆栈中的其他东西(比如用户身份验证状态?)
我取出了您的API请求,并用一个简单的Future
替换了它,该请求保证只响应以证明您的其余代码工作正常(确实如此)
您可能的解决方案是:
按照另一位回答者的建议,使用@StateObject
将API调用移动到视图层次结构中存储在较高位置的observateObject
(例如,EnvironmentObject
),该对象将传递给treatmentcardwiew
,而不是creati