![](/img/trans.png)
[英]ForEach not working in SwiftUI after conforming to Identifiable protocol
[英]SwiftUI identifiable ForEach doesn't update when initially starting with empty
我有一个 SwiftUI 应用程序,它在视图出现时从后端获取一些信息,然后尝试通过在ObservableObject
中设置@Published
vars 来更新 State 。 我遇到的问题是它在第一次获取时没有更新(它仍然是空的,因为它是用一个空数组初始化的)但是如果我点击另一个视图并返回它就会更新(因为已经获取了信息)。
显然,我使用@Published
的目的是在获取信息后更新视图。 这是一个更大的应用程序的一部分,但我有我下面的缩减版本。
首先,我们有一个包含我要更新的视图的父视图。
struct ParentView: View {
var body: some View {
NavigationView {
ScrollView {
VStack {
SummaryView()
// In real life I have various forms of summary
// but to simplify here I will just use this one SummaryView.
SummaryView()
SummaryView()
}
}
}
}
}
这是摘要视图本身:
struct SummaryView: View {
@ObservedObject var model = AccountsSummaryViewModel()
var body: some View {
VStack(alignment: .leading) {
HStack {
Text("Accounts")
.font(.title)
Spacer()
NavigationLink(
destination: AccountView(),
label: {
Image("RightArrow")
})
}
if model.accounts.count > 0 {
Divider()
}
// And if I add the following line for debugging
//Text(model.accounts.count)
// It remains 0.
ForEach(model.accounts, id: \.id) { account in
Text(account.account.text)
}
}
.padding()
.onAppear() {
model.onAppear()
}
}
}
这是 model 的简单视图:
class AccountsSummaryViewModel: ObservableObject, Identifiable {
@Published var accounts: [AccountIdentifiable] = []
func onAppear() {
AccountsService.accounts { (success, error, response) in
DispatchQueue.main.async {
// This always succeeds
if let response = response {
// All AccountIdentifiable does is make a struct that is Identifiable (has an account and a var id = UUID())
self.accounts = Array(response.accounts.map { AccountIdentifiable(account: $0) }.prefix(3))
}
}
}
}
}
这里也是 AccountsService 的内容,我会注意到 URL 是一个本地主机,但我不确定这是否重要:
public struct AccountsService {
public static func accounts(completion: @escaping ((Bool, Error?, AccountsResponse?) -> Void)) {
guard let url = getAllAccountsURL() else {
completion(false, nil, nil)
return
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.allHTTPHeaderFields = ["Content-Type": "application/json",
BusinessConstants.SET_COOKIE : CredentialsObject.shared.jwt]
let task = URLSession.shared.dataTask(with: request) { (data, urlResponse, error) in
guard let data = data else {
completion(false, error, nil)
return
}
guard let response = try? JSONDecoder().decode(AccountsResponse.self, from: data) else {
completion(false, error, nil)
return
}
// This does successfully decode and return here.
completion(true, nil, response)
return
}
task.resume()
}
private static func getAllAccountsURL() -> URL? {
let address = "\(BusinessConstants.SERVER)/plaid/accounts"
return URL(string: address)
}
}
我已经读到空的ScrollView
存在问题,但是,我的ScrollView
永远不会为空,因为我有那些 static 文本元素。 我还读到,如果您使用没有id的ForEach
,它可能会失败 - 但您可以看到我正在使用id ,所以我有点不知所措。
我在onAppear()
中有打印语句,所以我知道它运行并成功设置了@Published accounts
,但查看 UI 并将断点放在ForEach
中我可以看到视图没有更新。 但是,如果我在我的应用程序中导航到其他地方,然后返回到ParentView
,那么由于@Published accounts
是非空的(已经获取)它会完美更新。
看起来您遇到了问题,因为观察到的对象有两个级别, model: AccountsSummaryViewModel
包含accounts: [AccountIdentifiable]
。
SwiftUI 只会观看一个级别,导致当accounts
设置为多个UI 级别时,您的ParentView
不会更新。
如这里所讨论的,一种选择是通过 Xcode 中的 Swift Package 管理器使用PublishedObject 。 将model
中的SummaryView
更改为@PublishedObject
可能就是解决此问题所需的全部内容。
它不起作用的原因是我在 SummaryView 中使用了@ObservedObject
而不是@StateObject
。 进行更改解决了问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.