SwiftUI:视图模型不更新视图
SwiftUI: View Model does not update the View
我尝试用 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 中,List
在 NewChatView
.
中仍然为空
View-Model里面的print(self.idList)
语句显示了正确的idList
,但是它没有更新NewChatView
里面的List
。
您首先需要创建自己的自定义 Identifiable
和 Hashable
模型以在 List
或 ForEach
中显示 searchValue
。
像这样:
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: [=11=].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()
}
}
我尝试用 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 中,List
在 NewChatView
.
View-Model里面的print(self.idList)
语句显示了正确的idList
,但是它没有更新NewChatView
里面的List
。
您首先需要创建自己的自定义 Identifiable
和 Hashable
模型以在 List
或 ForEach
中显示 searchValue
。
像这样:
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: [=11=].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()
}
}