Published/Observed var 未在视图 swiftui 中更新 w/ 调用的函数

Published/Observed var not updating in view swiftui w/ called function

努力在 swiftui 中得到一个简单的例子和​​ 运行:

  1. 加载默认列表视图(工作)
  2. 单击启动 picker/filtering 选项的按钮(有效)
  3. select 选项,然后单击按钮关闭并使用 selected 选项调用函数(调用正常)
  4. 显示新的对象列表return来自调用(不工作)

我卡在 #4 上,returned 查询没有进入视图。我怀疑我在步骤 #3 中进行调用时正在创建一个不同的实例,但这对我来说没有意义 where/how/why 这很重要。

我试着简化了一些代码,但还是有点,很抱歉。

感谢任何帮助!

带 HStack 的主视图和用于过滤的按钮:

import SwiftUI
import FirebaseFirestore

struct TestView: View {
    @ObservedObject var query = Query()
    @State var showMonPicker = false
    @State var monFilter = "filter"

    
    var body: some View {
        VStack {
            HStack(alignment: .center) {
                Text("Monday")
                Spacer()
                Button(action: {
                    self.showMonPicker.toggle()
                }, label: {
                    Text("\(monFilter)")
                })
            }
            .padding()
            
            ScrollView(.horizontal) {
                LazyHStack(spacing: 35) {
                    ForEach(query.queriedList) { menuItems in
                        MenuItemView(menuItem: menuItems)
                    }
                }
            }
        }
        .sheet(isPresented: $showMonPicker, onDismiss: {
            //optional function when picker dismissed
        }, content: {
            CuisineTypePicker(selectedCuisineType: $monFilter)
        })
    }
}

调用包含所有结果的基本查询的 Query() 文件,以及 return 特定结果的可选函数:

import Foundation
import FirebaseFirestore

class Query: ObservableObject {
    
    @Published var queriedList: [MenuItem] = []
    
    init() {
        baseQuery()
    }
    
    func baseQuery() {
      let queryRef = Firestore.firestore().collection("menuItems").limit(to: 50)
        
        queryRef
            .getDocuments() { (querySnapshot, err) in
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
                self.queriedList = querySnapshot?.documents.compactMap { document in
                    try? document.data(as: MenuItem.self)
                    
                } ?? []
            }
        }
    }
    
    func filteredQuery(category: String?, glutenFree: Bool?) {
        var filtered = Firestore.firestore().collection("menuItems").limit(to: 50)

      // Sorting and Filtering Data
        if let category = category, !category.isEmpty {
          filtered = filtered.whereField("cuisineType", isEqualTo: category)
        }

        if let glutenFree = glutenFree, !glutenFree {
          filtered = filtered.whereField("glutenFree", isEqualTo: true)
        }

      filtered
            .getDocuments() { (querySnapshot, err) in
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
                self.queriedList = querySnapshot?.documents.compactMap { document in
                    try? document.data(as: MenuItem.self);
                } ?? []
                print(self.queriedList.count)
            }
        }
    }
}

我调用筛选查询的选择器视图:

import SwiftUI

struct CuisineTypePicker: View {
    
    @State private var cuisineTypes = ["filter", "American", "Chinese", "French"]
    @Environment(\.presentationMode) var presentationMode

    @Binding var selectedCuisineType: String
    @State var gfSelected = false
    
    let query = Query()
       
    var body: some View {
        VStack(alignment: .center) {
            //Buttons and formatting code removed to simplify.. 
            }
            .padding(.top)
            
            Picker("", selection: $selectedCuisineType) {
                ForEach(cuisineTypes, id: \.self) {
                    Text([=13=])
                }
            }
            Spacer()
            Button(action: {
                self.query.filteredQuery(category: selectedCuisineType, glutenFree: gfSelected)
                
                self.presentationMode.wrappedValue.dismiss()
                
            }, label: {
                Text( "apply filters")
            })               
        }
        .padding()
    }
}

我怀疑问题是因为您没有在 TestViewCuisineTypePicker 之间共享 Query 的同一个实例。因此,当您对 CuisineTypePicker 中包含的实例启动新的 Firebase 查询时,结果永远不会反映在主视图中。

这是一个如何解决这个问题的例子(Firebase 代码现在被一些非异步示例代码替换):

struct MenuItem : Identifiable {
    var id = UUID()
    var cuisineType : String
    var title : String
    var glutenFree : Bool
}

struct ContentView: View {
    @ObservedObject var query = Query()
    @State var showMonPicker = false
    @State var monFilter = "filter"
    
    var body: some View {
        VStack {
            HStack(alignment: .center) {
                Text("Monday")
                Spacer()
                Button(action: {
                    self.showMonPicker.toggle()
                }, label: {
                    Text("\(monFilter)")
                })
            }
            .padding()
            
            ScrollView(.horizontal) {
                LazyHStack(spacing: 35) {
                    ForEach(query.queriedList) { menuItem in
                        Text("\(menuItem.title) - \(menuItem.cuisineType)")
                    }
                }
            }
        }
        .sheet(isPresented: $showMonPicker, onDismiss: {
            //optional function when picker dismissed
        }, content: {
            CuisineTypePicker(query: query, selectedCuisineType: $monFilter)
        })
    }
}

class Query: ObservableObject {
    
    @Published var queriedList: [MenuItem] = []
    
    private let allItems: [MenuItem] = [.init(cuisineType: "American", title: "Hamburger", glutenFree: false),.init(cuisineType: "Chinese", title: "Fried Rice", glutenFree: true)]
    
    init() {
        baseQuery()
    }
    
    func baseQuery() {
        self.queriedList = allItems
    }
    
    func filteredQuery(category: String?, glutenFree: Bool?) {
        queriedList = allItems.filter({ item in
            if let category = category {
                return item.cuisineType == category
            } else {
                return true
            }
        }).filter({item in
            if let glutenFree = glutenFree {
                return item.glutenFree == glutenFree
            } else {
                return true
            }
        })
    }
}

struct CuisineTypePicker: View {
    @ObservedObject var query : Query
    @Binding var selectedCuisineType: String
    @State private var gfSelected = false
    
    private let cuisineTypes = ["filter", "American", "Chinese", "French"]
    @Environment(\.presentationMode) private var presentationMode
    
    var body: some View {
        VStack(alignment: .center) {
            //Buttons and formatting code removed to simplify..
        }
        .padding(.top)
        
        Picker("", selection: $selectedCuisineType) {
            ForEach(cuisineTypes, id: \.self) {
                Text([=10=])
            }
        }
        Spacer()
        Button(action: {
            self.query.filteredQuery(category: selectedCuisineType, glutenFree: gfSelected)
            self.presentationMode.wrappedValue.dismiss()
        }, label: {
            Text( "apply filters")
        })
    }
}