SwiftUI:视图模型不会更新视图

时间:2020-08-21 14:46:27

标签: mvvm swiftui full-text-search algolia

我尝试使用Algolia实现搜索栏,并使用MVVM模式。

这是我的视图模型:

class AlgoliaViewModel: ObservableObject {
   
   @Published var idList = [String]()

   func searchUser(text: String){
       
           let client = SearchClient(appID: "XXX", apiKey: "XXX")
           let index = client.index(withName: "Users")
           let query = Query(text)
           index.search(query: query) { result in
             if case .success(let response) = result {
               print("Response: \(response)")
               
                do {
                   let hits: Array = response.hits
                   var idList = [String]()
                   for x in hits {
                       idList.append(x.objectID.rawValue)
                   }
                   
                   DispatchQueue.main.async {
                       self.idList = idList
                       print(self.idList)
                   }
               }
               catch {
                   print("JSONSerialization error:", error)
               }
             }
       }
   
   }
   
}

这是我的观点:

struct NewChatView : View {
    
    @State private var searchText = ""
    
    @ObservedObject var viewModel = AlgoliaViewModel()
    
    var body : some View{
        
        VStack(alignment: .leading){
            
            Text("Select To Chat").font(.title).foregroundColor(Color.black.opacity(0.5))
            
            ScrollView(.vertical, showsIndicators: false) {
                
                VStack(spacing: 12){
                    
                    HStack {
                        TextField("Start typing",
                                  text: $searchText,
                                  onCommit: { self.viewModel.searchUser(text: self.searchText) })
                            .textFieldStyle(RoundedBorderTextFieldStyle())
                        Button(action: {
                            self.viewModel.searchUser(text: self.searchText)
                        }) {
                            Image(systemName: "magnifyingglass")
                        }
                    }   .padding()
                    
                    List {
                        
                        ForEach(viewModel.idList, id: \.self){ i in
                            Text(i)
                        }
                        
                    }
                    
                }
            }
        }.padding()
    }
    
}

我经常在Firebase中使用这种模式,并且一切正常,但是在Algolia中,ListNewChatView中仍然为空。

视图模型内的print(self.idList)语句显示正确的idList,但不会更新List内的NewChatView

1 个答案:

答案 0 :(得分:2)

您首先需要创建自己的自定义IdentifiableHashable模型,以在searchValueList中显示ForEach

类似这样的东西:

struct MySearchModel: Identifiable, Hashable {
    let id = UUID().uuidString
    let searchValue: String
}

然后在您的AlgoliaViewModel中使用它。设置一个空数组的默认值。 您还可以映射收到的hits并将其转换为新模型。不需要多余的for循环。

class AlgoliaViewModel: ObservableObject {
    
    @Published var idList: [MySearchModel] = []
    
    func searchUser(text: String) {
        
        let client = SearchClient(appID: "XXX", apiKey: "XXX")
        let index = client.index(withName: "Users")
        let query = Query(text)
        
        index.search(query: query) { result in
            
            if case .success(let response) = result {
                print("Response: \(response)")
                
                do {
                    let hits: Array = response.hits
                    
                    DispatchQueue.main.async {
                        self.idList = hits.map({ MySearchModel(searchValue: $0.objectID.rawValue) })
                        print(self.idList)
                    }
                }
                catch {
                    print("JSONSerialization error:", error)
                }
            }
        }
    }
}

对于NewChatView,您可以删除ScrollView,因为它与当前VStack中的元素冲突,并且也将List隐藏在结果中。以下更改应显示所有结果。

struct NewChatView : View {

    @State private var searchText = ""
    @ObservedObject var viewModel = AlgoliaViewModel()

    var body: some View{

        VStack(alignment: .leading) {

            Text("Select To Chat")
                .font(.title)
                .foregroundColor(Color.black.opacity(0.5))

            VStack {
                HStack {
                    TextField("Start typing",
                              text: $searchText,
                              onCommit: { self.viewModel.searchUser(text: self.searchText) 
                        })
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                    Button(action: {
                        self.viewModel.searchUser(text: self.searchText)
                    }) {
                        Image(systemName: "magnifyingglass")
                    }
                }   .padding()

                List {
                    ForEach(viewModel.idList) { i in
                        Text(i.searchValue)
                            .foregroundColor(Color.black)
                    }
                }
            }
        }.padding()
    }
}