已发布/观察到的 var 在视图 swiftui 中未更新,带有调用函数

Posted

技术标签:

【中文标题】已发布/观察到的 var 在视图 swiftui 中未更新,带有调用函数【英文标题】:Published/Observed var not updating in view swiftui w/ called function 【发布时间】:2021-09-28 18:41:27 【问题描述】:

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

    加载默认列表视图(工作) 点击按钮启动选择器/过滤选项(工作) 选择选项,然后单击按钮以关闭并使用所选选项调用函数(调用正常) 显示调用返回的新对象列表(不工作)

我被困在#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()
    

【问题讨论】:

我对“应用过滤器”按钮有点困惑。在内部,您在query 上执行filteredQuery,它没有被注释为@ObservedObject@StateObject。然后,它关闭演示模式,因此视图将消失,因此Query 的实例将永远不会被读取。也许问题在于您的意思是在TestViewCuisineTypePicker 之间使用Query相同实例,但实际上并没有在两者之间共享它? @jnpdx 是的!这听起来像我的想法,但我无法理解,我看到你在下面发布了一个解决方案,我会深入研究。感谢您的帮助! 【参考方案1】:

我怀疑问题源于您没有在 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($0)
            
        
        Spacer()
        Button(action: 
            self.query.filteredQuery(category: selectedCuisineType, glutenFree: gfSelected)
            self.presentationMode.wrappedValue.dismiss()
        , label: 
            Text( "apply filters")
        )
    

【讨论】:

感谢您抽出宝贵时间,Query 的共享实例正是我出错的地方。我尝试了一些东西,但没有点击我需要将实例作为观察对象传递给选取器。非常感谢!

以上是关于已发布/观察到的 var 在视图 swiftui 中未更新,带有调用函数的主要内容,如果未能解决你的问题,请参考以下文章

观察到的对象无意中被解雇 SwiftUI

在 SwiftUI 中使用可观察对象切换视图

观察自定义视图 SwiftUI 中的属性变化

SwiftUI MVVM:父视图更新时重新初始化子视图模型

观察 SwiftUI 中的帧变化

为啥我的 SwiftUI 视图不会在更新 @State var 时更新?