【问题标题】:SwiftUI - How to pass date string from datepicker back to view modelSwiftUI - 如何将日期字符串从日期选择器传回查看模型
【发布时间】:2021-10-04 17:40:00
【问题描述】:

我想要完成的事情

我有一个表示日期选择器的视图和另一个表示特定日期的锻炼课程列表的视图。当用户更改日期选择器视图中的日期时,我想更新锻炼列表。我正在遵循 MVVM 设计,我基本上希望在我的视图模型中保持状态(日期字符串和与该日期相关的锻炼列表)并将其传递给我的视图。

当用户通过日期选择器选择一个新日期并让它在另一个视图中填充该字符串时,我想首先在我的视图模型中更新我的 stateDate 变量。最终目标是更新我从中提取数据的 Firestore 中的路径,以便我可以提取新数据。

当用户在我的一个视图中通过日期选择器选择新日期时,如何更新视图模型中的 @Published 变量?

源代码片段

WorkoutDatePicker 视图

import SwiftUI

struct WorkoutDatePicker: View {
    @ObservedObject var viewModel = WorkoutClassViewModel()
    var body: some View {
        Form {
            DatePicker("Date",
                       selection: $viewModel.stateDate,
                       displayedComponents: [.date])
            Text("Your current date is \(viewModel.stateDate)")
        }   
    }
}

WorkoutClassViewModel 视图模型

import Foundation
import FirebaseFirestore

class WorkoutClassViewModel: ObservableObject {
    
    @Published var workoutClasses = [WorkoutClass]()
    @Published var stateDate = Date()
    
    private var db = Firestore.firestore()
    
    func fetchData() {
//        let dateString = getDateString(date: userData.date,format: "MM-dd-YYYY")
//        db.collection("schedules/\(dateString)/classes").addSnapshotListener { (querySnapshot, error) in
        db.collection("schedules/2021-07-18/classes").addSnapshotListener { (querySnapshot, error) in
            guard let documents = querySnapshot?.documents else {
                print("No documents")
                return
            }
            
            self.workoutClasses = documents.map { (queryDocumentSnapshot) -> WorkoutClass in
                let data = queryDocumentSnapshot.data()
                let id = queryDocumentSnapshot.documentID
                let reservationCnt = data["reservationCnt"] as? Int ?? 0
                let workoutType = data["workoutType"] as? String ?? ""
                return WorkoutClass(id: id, reservationCnt: reservationCnt,  workoutType: workoutType)
            }
        }
    }

}

类视图视图

import SwiftUI

struct ClassView: View {
    @ObservedObject var viewModel = WorkoutClassViewModel()
    var body: some View {
        VStack{
            WorkoutDatePicker()
            
            NavigationView {
                List(viewModel.workoutClasses) { workoutClass in
                    VStack(alignment: .leading) {
                        Text(workoutClass.workoutType).font(.title)
                    }
                }.onAppear() {
                    self.viewModel.fetchData()
                }
            }
        }    
    }
}

我是 SwiftUI 和 iOS 开发的新手,所以任何建议都是好建议!

【问题讨论】:

    标签: mvvm swiftui


    【解决方案1】:

    现在,您有两个不同的 WorkoutClassViewModel 实例。当一个更新时,另一个不知道日期已更改。为了解决这个问题,您可以通过传递对子视图 (WorkoutDatePicker) 的引用来共享相同的 WorkoutClassViewModel。关于这个问题的另一个答案对此进行了说明。

    另一种选择,将子视图的职责与仅选择日期分开,将@Binding 传递给只是子视图可以使用的stateDate: p>

    struct WorkoutDatePicker: View {
        @Binding var date : Date //<-- Here
        var body: some View {
            Form {
                DatePicker("Date",
                           selection: $date,
                           displayedComponents: [.date])
                Text("Your current date is \(date)")
            }
        }
    }
    
    struct ClassView: View {
        @StateObject private var viewModel = WorkoutClassViewModel() //<-- Here
        var body: some View {
            VStack{
                WorkoutDatePicker(date: $viewModel.stateDate)
                Text("Date: \(viewModel.stateDate)")
                
                NavigationView {
                    List(viewModel.workoutClasses) { workoutClass in
                        VStack(alignment: .leading) {
                            Text(workoutClass.workoutType).font(.title)
                        }
                    }.onAppear() {
                        self.viewModel.fetchData()
                    }
                }
            }
        }
    }
    

    【讨论】:

    • 感谢您的帮助,我实施了您建议的更改!之后,通过在 onAppear() ``` .onChange(of: viewModel.stateDate, perform: { value in self.viewModel.fetchData() } ) ``` 我仍然可以让这个更干净,但这对实现这个功能有很大帮助,谢谢!!
    【解决方案2】:

    试试这些源 sn-ps:

    class WorkoutClassViewModel: ObservableObject {
        
        @Published var workoutClasses = [WorkoutClass]()
        @Published var stateDate = Date()
        
        private var db = Firestore.firestore()
        
        func fetchData(completion: @escaping (WorkoutClass?) -> Void) {  // <--- here
    //        let dateString = getDateString(date: userData.date,format: "MM-dd-YYYY")
    //        db.collection("schedules/\(dateString)/classes").addSnapshotListener { (querySnapshot, error) in
            db.collection("schedules/2021-07-18/classes").addSnapshotListener { (querySnapshot, error) in
                guard let documents = querySnapshot?.documents else {
                    print("No documents")
                    completion(nil) // <--- here
                    return
                }
                
                self.workoutClasses = documents.map { (queryDocumentSnapshot) -> WorkoutClass in
                    let data = queryDocumentSnapshot.data()
                    let id = queryDocumentSnapshot.documentID
                    let reservationCnt = data["reservationCnt"] as? Int ?? 0
                    let workoutType = data["workoutType"] as? String ?? ""
                    // --- here
                    completion(WorkoutClass(id: id, reservationCnt: reservationCnt,  workoutType: workoutType))
                }
            }
        }
        
    
    }
    struct ClassView: View {
        @StateObject var viewModel = WorkoutClassViewModel() // <--- here
        var body: some View {
            VStack{
                WorkoutDatePicker(viewModel: viewModel)  // <--- here
                
                NavigationView {
                    List(viewModel.workoutClasses) { workoutClass in
                        VStack(alignment: .leading) {
                            Text(workoutClass.workoutType).font(.title)
                        }
                    }.onAppear() {
                        self.viewModel.fetchData()
                    }
                }
            }
        }
    }
    struct WorkoutDatePicker: View {
        @ObservedObject var viewModel: WorkoutClassViewModel  // <--- here
        var body: some View {
            Form {
                DatePicker("Date",
                           selection: $viewModel.stateDate,
                           displayedComponents: [.date])
                Text("Your current date is \(viewModel.stateDate)")
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-08
      • 2015-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-27
      相关资源
      最近更新 更多