首页 > 解决方案 > 已发布/观察到的 var 未在视图 swiftui 中更新,带调用函数

问题描述

努力在 swiftui 中启动并运行一个简单的示例:

  1. 加载默认列表视图(工作)
  2. 单击启动选择器/过滤选项的按钮(工作)
  3. 选择选项,然后单击按钮以关闭并使用所选选项调用功能(通话正常)
  4. 显示从调用返回的新对象列表(不工作)

我被困在#4,返回的查询没有进入视图。我怀疑我在第 3 步中进行调用时创建了一个不同的实例,但这对我来说在哪里/如何/为什么重要是没有意义的。

我试图将代码简化一些,但还是有点,抱歉。

感谢任何帮助!

带有 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() 文件调用包含所有结果的基本查询,以及返回特定结果的可选函数:

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($0)
                }
            }
            Spacer()
            Button(action: {
                self.query.filteredQuery(category: selectedCuisineType, glutenFree: gfSelected)
                
                self.presentationMode.wrappedValue.dismiss()
                
            }, label: {
                Text( "apply filters")
            })               
        }
        .padding()
    }
}

标签: swiftuiobservedobject

解决方案


我怀疑问题源于您没有Query在您的TestView和您的CuisineTypePicker. 因此,当您对 中包含的实例启动新的 Firebase 查询时CuisineTypePicker,结果永远不会反映在主视图中。

这是一个如何解决这个问题的示例(现在将 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($0)
            }
        }
        Spacer()
        Button(action: {
            self.query.filteredQuery(category: selectedCuisineType, glutenFree: gfSelected)
            self.presentationMode.wrappedValue.dismiss()
        }, label: {
            Text( "apply filters")
        })
    }
}

推荐阅读