繁体   English   中英

SwiftUI 可识别的 ForEach 在最初以空开头时不会更新

[英]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 文本元素。 我还读到,如果您使用没有idForEach ,它可能会失败 - 但您可以看到我正在使用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM