简体   繁体   English

跨 Swift 中的多个选项卡访问相同的视图 Model

[英]Access same View Model across multiple tabs in Swift

Background背景

I have a workoutVM view model that would hold most of my view models, which I want to pass down to all other view controllers in my app.我有一个锻炼VM 视图workoutVM可以容纳我的大部分视图模型,我想将其传递给我的应用程序中的所有其他视图控制器。 My app also has a tabbar controller, which I have used to store some data such as user information, etc.我的应用程序还有一个标签栏 controller,我用它来存储一些数据,例如用户信息等。

Problem 1问题 1

在此处输入图像描述

Even though I have created the workoutVM view model (with values) in the MyTrainingViewController ("Home" tab), I am unable to pass the view model (with values) to the next ExerciseSetsViewController ("Workout" tab) by using即使我在MyTrainingViewController (“主页”选项卡)中创建了workoutVM视图 model(带有值),我也无法通过使用将视图 model(带有值)传递到下一个ExerciseSetsViewController集视图控制器(“锻炼”选项卡)

  1. Method 1 - By using Delegate MyTrainingViewControllerDelegate and/or方法 1 - 通过使用委托MyTrainingViewControllerDelegate和/或
  2. Method 2 - By instantiating ExerciseSetsViewController and loading the view.方法 2 - 通过实例化ExerciseSetsViewController并加载视图。

The codes/action to pass workoutVM view model are run when user selects a particular tableview cell.当用户选择特定的表格视图单元格时,将运行通过锻炼VM 视图workoutVM的代码/操作。

I am really not sure why either one of the methods work because similar approach worked for many other scenarios.我真的不确定为什么其中任何一种方法都有效,因为类似的方法适用于许多其他场景。 Below is the Xcode debugger showing that my codes to utilize both methods didn't pass the workoutVM view model to the ExerciseSetsViewController下面是 Xcode 调试器,显示我使用这两种方法的代码没有将锻炼虚拟机视图 model 传递给ExerciseSetsViewController

在此处输入图像描述

Problem 2问题 2

As a result, I found a workaround (Method 3 that were commented out in the below codes) to utilize tabbar to store the workoutVM view model (again relying on tabbar to pass & share data across multiple view controllers).结果,我找到了一种解决方法(方法 3 在下面的代码中被注释掉了)来利用 tabbar 来存储锻炼虚拟机视图workoutVM (再次依靠 tabbar 在多个视图控制器之间传递和共享数据)。

At this point, I am afraid that my app is practically using tabbar as a "singleton", even though I "sort of" understand that it is not quite "singleton".在这一点上,我担心我的应用实际上是在使用 tabbar 作为“单例”,尽管我“有点”理解它不是完全“单例”。

I think, ideally, the view models should serve as some sort of data models, which are to be shared/manipulated/passed across multiple view controllers without the need to have tabbar as a middle layer.我认为,理想情况下,视图模型应该充当某种数据模型,它们可以在多个视图控制器之间共享/操作/传递,而无需将标签栏作为中间层。 Wouldn't that be correct?那不是正确的吗? Or is this the best/good practice that I am adopting by utilizing the tabbar?或者这是我通过使用标签栏采用的最佳/良好做法?

protocol MyTrainingViewControllerDelegate {
    func passWorkoutVM(workoutVM: WorkoutViewModel)
}

class MyTrainingViewController: UIViewController {

    var workoutVM: WorkoutViewModel?
    
    var delegate: MyTrainingViewControllerDelegate!

    @IBOutlet weak var dayProgramTableView: UITableView!

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)
     
        dayProgramTableView.delegate = self
        dayProgramTableView.dataSource = self
      }
}

extension MyTrainingViewController: UITableViewDelegate {
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
     
        let tabbar = tabBarController as! MainTabBarController

        let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let ExerciseSetsViewController = storyBoard.instantiateViewController(withIdentifier: "ExerciseSetsView") as! ExerciseSetsViewController

        guard let workoutVM = self.workoutVM else {
            return
        }
        
        print("printing workoutViewModel dayprogram no.of exercise at HomeView \(workoutVM.dayprograms[indexPath.row].dayprogram.dayIntensity)")
        
        //1.Instantiate view via storyboard Method
        ExerciseSetsViewController.loadViewIfNeeded()
        ExerciseSetsViewController.workoutVM = workoutVM
        
        //2.Delegate Method
        self.delegate?.passWorkoutVM(workoutVM: workoutVM)
        
        //3.Tabbar Method
//        tabbar.workoutVM = workoutVM

        tabbar.selectedIndex = 1
    }
}
class ExerciseSetsViewController: UIViewController {
      var workoutVM: WorkoutViewModel?
      
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)

        // ** To do
        //create workoutViewModel
         //3.Tabbar Method
//        self.workoutVM = tabbar.workoutVM

        print("printing workoutViewModel to check if workoutVM had been passed from MyTrainingView \(String(describing: self.workoutVM))")

  }
  
}

extension ExerciseSetsViewController: MyTrainingViewControllerDelegate {
    
    func passWorkoutVM(workoutVM: WorkoutViewModel) {
        self.workoutVM = workoutVM
        print("passWorkoutDelegate Method executed")
    }
    
}
class MainTabBarController: UITabBarController {

    var workoutVM: WorkoutViewModel?

    override func viewDidLoad() {
        super.viewDidLoad()
    }

}

With some external help, I was able to identify the source of the 1st problem and rectify them.在一些外部帮助下,我能够确定第一个问题的根源并加以纠正。 I was also able to hear necessary advice for the 2nd problem.我还能够听到关于第二个问题的必要建议。

Problem 1问题 1

The issue was that the exerciseSetsViewController that I "instantiated via storyboard" was different from the exerciseSetsViewController that would have been shown from TabbarController 's 2nd tab (Workout Tab).问题是我“通过故事板实例化”的exerciseSetsViewControllerTabbarController的第二个选项卡(锻炼选项卡)中显示的exerciseSetsViewController不同。 Hence, the workoutVM was not passed to correct viewcontroller.因此, workoutVM没有传递给正确的视图控制器。 Hence, below corrected codes needed to be used if I wanted to use either因此,如果我想使用以下任何一个,则需要使用以下更正的代码

  1. Method 1 - By using Delegate MyTrainingViewControllerDelegate and/or方法 1 - 通过使用委托MyTrainingViewControllerDelegate和/或
  2. Method 2 - By instantiating ExerciseSetsViewController and loading the view.方法 2 - 通过实例化ExerciseSetsViewController并加载视图。

The corrected code ensured that the exerciseSetsViewController that was instantiated was placed as the TabbarController 's 2nd tab.更正后的代码确保实例化的exerciseSetsViewController被放置为TabbarController的第二个选项卡。

protocol MyTrainingViewControllerDelegate {
    func passWorkoutVM(workoutVM: WorkoutViewModel)
}

class MyTrainingViewController: UIViewController {

    var workoutVM: WorkoutViewModel?
    
    var delegate: MyTrainingViewControllerDelegate!

    @IBOutlet weak var dayProgramTableView: UITableView!

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)
     
        dayProgramTableView.delegate = self
        dayProgramTableView.dataSource = self
      }
}

extension MyTrainingViewController: UITableViewDelegate {
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
     
        let tabbar = tabBarController as! MainTabBarController

        let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let ExerciseSetsViewController = storyBoard.instantiateViewController(withIdentifier: "ExerciseSetsView") as! ExerciseSetsViewController

        guard let workoutVM = self.workoutVM else {
            return
        }
                let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let exerciseSetsViewController = storyBoard.instantiateViewController(withIdentifier: "ExerciseSetsView") as! ExerciseSetsViewController

        guard let nav = tabbar.viewControllers?[1] as? UINavigationController, let exerciseSetsViewController = nav.viewControllers.first as? ExerciseSetsViewController else { return }
        
        print("printing workoutViewModel dayprogram no.of exercise at MyTrainingView \(workoutVM.dayprograms[indexPath.row].dayprogram.dayIntensity)")
        
//        1.Instantiate view via storyboard Method
        exerciseSetsViewController.loadViewIfNeeded()
        exerciseSetsViewController.workoutVM = workoutVM

//        2.Delegate Method
        self.delegate?.passWorkoutVM(workoutVM: workoutVM)
}

Problem 2问题 2

I was advised that my approach to utilize the "tabbar" is literally the same as using "Singleton" because there is one shared source of data where multiple views are accessing.我被告知我使用“标签栏”的方法与使用“单例”的方法实际上是相同的,因为有一个共享的数据源可以访问多个视图。 Likewise, my approach to utilize view models that can be accessed via multiple view controller is same as using "global" variable , which has similar repercussion as using "Singletons".同样,我利用可通过多视图 controller 访问的视图模型的方法与使用“全局”变量相同,这与使用“单例”具有相似的影响。

While this approach can be acceptable in certain cases, it is not the "best practice" and I will need to change some of my codes/approach, where each view controller would have their own set of data/view models.虽然这种方法在某些情况下是可以接受的,但这不是“最佳实践” ,我需要更改我的一些代码/方法,其中每个视图 controller 都有自己的一组数据/视图模型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM