SwiftUI中的onAppear()中的ViewModel数据已更改

时间:2020-09-14 23:02:34

标签: mvvm swiftui reactive-programming swiftui-list

我正在尝试从View内部.onAppear()中的viewModel中调用方法。该方法执行后,看起来数据已删除。我看到不到1秒钟的时间即可正确显示数据,但是此后UI仍未显示任何内容并且数据为空。如果我在按钮内调用该数据,则它可以正常工作,完全没有问题。您能帮我吗,onAppear()内的调用方法有什么问题?这是我的视图代码:

struct DomainsOfInterestView: View {
    @EnvironmentObject var userProfile: UserProfile
    @ObservedObject var viewModel: DomainsViewModel = DomainsViewModel()
    
    var body: some View {
            VStack(alignment: .center) {
                HStack {
                    Text("Choose domains of interest for your profile")
                        .foregroundColor(Color.orGrayColor)
                        .font(.custom("SFProDisplay-Regular", size: 16))
                    Spacer()
                }.padding([.bottom, .top], 16)
                VStack {
                    Multiselect(items: $viewModel.selectableDomains) { domain in
                        HStack {
                            if domain.isSelected {
                                Image(uiImage: #imageLiteral(resourceName: "check-box-active-2-1")).renderingMode(.original)
                                Text(domain.domain.name)
                                .foregroundColor(Color.black)
                                Spacer()
                            } else {
                                Image(uiImage: #imageLiteral(resourceName: "check-box-active-1")).renderingMode(.original)
                                Text(domain.domain.name)
                                .foregroundColor(Color.orGrayColor)
                                Spacer()
                            }
                            
                        }
                    }
                }.padding([.top, .bottom], 15)
                .background(Color.white)
                .cornerRadius(8)
                Spacer()
                OrangeButton(action: {
                    //                self.viewModel.getUserSelectableDomains(userDomains: self.userProfile.domainsOfInterest)
                }) {
                    Text("Save")
                }.padding([.leading, .trailing, .top], 30)
                    .padding(.bottom, 24)
            }.padding([.leading, .trailing], 12)
            .navigationBarTitle("Domains of interest")
            .background(Color.orBackgroundGrayColor.edgesIgnoringSafeArea(.all))
                .onAppear {
                    self.viewModel.getUserSelectableDomains(userDomains: self.userProfile.domainsOfInterest)
        }
    }
}
//
//struct DomainsOfInterestView_Previews: PreviewProvider {
//    static var previews: some View {
//    DomainsOfInterestView()
//    }
//}

struct Multiselect<T: Selectable, V: View>: View {
    @Binding var items: [T]
    var rowBuilder: (T) -> V

    var body: some View {
        ForEach(items.indices,id: \.self) { index in
            VStack {
                Button(action: { self.items.toggleSelected(self.items[index]) }) {
                    self.rowBuilder(self.items[index])
                }
                if index != self.items.count - 1 {
                    Divider()
                }
            }.padding([.trailing, .leading], 12)
        }
    }
}

这是我的viewModel,带有方法getUserSelectableDomains(userDomains:[InterestDomain]),该方法在onAppear()内部调用:

class DomainsViewModel: ObservableObject {
    @Published var domains: [InterestDomain] = [InterestDomain]()
    @Published var userSelectedDomains: [InterestDomain : Bool] = [InterestDomain : Bool]()
    @Published var selectableDomains: [SelectableDomain] = []
    
    init() {
        self.getDomains { (response) in
            print(response)
        }
    }
    
    func getUserSelectableDomains( userDomains: [InterestDomain]) {
        for domain in domains{
            if userDomains.contains(where: { (userDomain) -> Bool in
                userDomain.uuid == domain.uuid
            })
                 {
                     self.selectableDomains.append(SelectableDomain(domain: domain, isSelected: true))
                 } else {
                     self.selectableDomains.append(SelectableDomain(domain: domain, isSelected: false))
                 }
        }
    }
    
    func getDomains(completion: @escaping (Bool) -> Void) {
        NetworkEngine.shared.appNetwork.getInterestDomains { result in
            self.domains.removeAll()
            switch result {
            case .success(let domainsOfInterest):
                if let domainsList = domainsOfInterest {
                    self.domains = domainsList
                }
                completion(domainsOfInterest != nil)
            case .failure(_):
                completion(false)
            }
        }
    }
}
viewModel中的

selectableDomains var是正在UI上显示的数据源。如果我在按钮而不是onAppear()上调用函数getUserSelectableDomains(userDomains:[InterestDomain]),则它工作正常,UI正在正确更新。如果我在onAppear()内调用它,最初它可以工作,但是不到1秒,此后什么也没显示,就像数据为空。

1 个答案:

答案 0 :(得分:0)

@Published属性需要在主线程上设置-在后台(例如,在异步任务中)更新它们时,请尝试使用DispatchQueue.main

func getDomains(completion: @escaping (Bool) -> Void) {
    NetworkEngine.shared.appNetwork.getInterestDomains { result in
        DispatchQueue.main.async {
            self.domains.removeAll()
        }
        switch result {
        case .success(let domainsOfInterest):
            if let domainsList = domainsOfInterest {
                DispatchQueue.main.async {
                    self.domains = domainsList
                }
            }
            completion(domainsOfInterest != nil)
        case .failure:
            completion(false)
        }
    }
}