在 Swift 中跨多个选项卡访问相同的视图模型

Posted

技术标签:

【中文标题】在 Swift 中跨多个选项卡访问相同的视图模型【英文标题】:Access same View Model across multiple tabs in Swift 【发布时间】:2022-01-23 21:37:57 【问题描述】:

背景

我有一个 workoutVM 视图模型,它可以容纳我的大部分视图模型,我想将其传递给我的应用程序中的所有其他视图控制器。我的应用还有一个tabbar控制器,我用它来存储一些数据,比如用户信息等。

问题 1

即使我在MyTrainingViewController(“主页”选项卡)中创建了workoutVM 视图模型(带有值),我也无法将视图模型(带有值)传递给下一个ExerciseSetsViewController("锻炼”选项卡)使用

    方法 1 - 通过使用委托MyTrainingViewControllerDelegate 和/或 方法 2 - 通过实例化 ExerciseSetsViewController 并加载视图。

当用户选择特定的表格视图单元格时,将运行传递workoutVM 视图模型的代码/操作。

我真的不确定为什么其中任何一种方法都有效,因为类似的方法适用于许多其他场景。下面是 Xcode 调试器,显示我使用这两种方法的代码没有将锻炼虚拟机视图模型传递给 ExerciseSetsViewController

问题 2

结果,我找到了一个解决方法(方法 3 在下面的代码中被注释掉了) 来利用 tabbar 来存储 workoutVM 视图模型(再次依靠 tabbar 来传递和分享跨多个视图控制器的数据)。

在这一点上,我担心我的应用实际上是在将 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()
    



【问题讨论】:

【参考方案1】:

在一些外部帮助下,我能够确定第一个问题的根源并加以纠正。我还能够听到关于第二个问题的必要建议。

问题 1

问题在于我“通过情节提要”“实例化”的 exerciseSetsViewControllerTabbarController 的第二个选项卡(锻炼选项卡)中显示的 exerciseSetsViewController 不同。因此,workoutVM 没有传递给正确的视图控制器。因此,如果我想使用以下更正的代码,则需要使用以下代码

    方法 1 - 通过使用委托MyTrainingViewControllerDelegate 和/或 方法 2 - 通过实例化 ExerciseSetsViewController 并加载 视图。

更正后的代码确保被实例化的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)

问题 2

我被告知我使用“标签栏”的方法实际上与使用“单例”的方法相同,因为有一个共享数据源可供多个视图访问。同样,我使用可通过多个视图控制器访问的视图模型的方法与使用“全局”变量相同,这与使用“单例”具有相似的影响。

虽然这种方法在某些情况下是可以接受的,但这不是“最佳实践”,我需要更改我的一些代码/方法,每个视图控制器都有自己的设置数据/视图模型。

【讨论】:

以上是关于在 Swift 中跨多个选项卡访问相同的视图模型的主要内容,如果未能解决你的问题,请参考以下文章

在 scikit-learn 中跨多个模型进行交叉验证时如何保持相同的折叠?

Swift 多个子视图并返回到原始 TableView

当我的应用程序有多个相同的选项卡时,如何访问选项卡中的组件?(如何确定活动选项卡?)

如何在1项标签栏控制器中制作顶部标签栏以在swift中显示多个视图控制器?

如何在 Shiny 中跨选项卡持久化 event_data?

再次按下时禁用所选选项卡的重新初始化