在 Swift 中跨多个选项卡访问相同的视图模型
Access same View Model across multiple tabs in Swift
背景
我有一个 workoutVM
视图模型,可以容纳我的大部分视图模型,我想将其传递给应用程序中的所有其他视图控制器。我的应用程序还有一个标签栏控制器,我用它来存储一些数据,比如用户信息等。
问题 1
即使我在 MyTrainingViewController
(“主页”选项卡)中创建了 workoutVM
视图模型(带值),我也无法将视图模型(带值)传递给接下来 ExerciseSetsViewController
(“锻炼”选项卡)使用
- 方法 1 - 使用委托
MyTrainingViewControllerDelegate
and/or
- 方法 2 - 通过实例化
ExerciseSetsViewController
并加载视图。
当用户选择特定的表格视图单元格时,传递 workoutVM
视图模型的 codes/action 是 运行。
我真的不确定为什么其中一种方法有效,因为类似的方法适用于许多其他情况。下面是 Xcode 调试器,显示我使用这两种方法的代码没有将 workoutVM 视图模型传递给 ExerciseSetsViewController
问题2
因此,我找到了一个解决方法(方法 3 在下面的代码中被注释掉了) 利用 tabbar 来存储 workoutVM
视图模型(再次依靠标签栏在多个视图控制器之间传递和共享数据)。
在这一点上,恐怕我的应用实际上是将 tabbar 用作“单例”,尽管我“有点”明白它并不完全是“单例”。
我认为,理想情况下,视图模型应该充当某种数据模型,它们将 shared/manipulated/passed 跨多个视图控制器,而不需要将标签栏作为中间层。 这不对吗?或者这是我通过使用标签栏采用的 best/good 做法?
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
问题是我“通过故事板实例化”的 exerciseSetsViewController
与 TabbarController
的第二个选项卡(锻炼选项卡)中显示的 exerciseSetsViewController
不同.因此,workoutVM
未被传递以更正 viewcontroller。因此,如果我想使用
,则需要使用下面更正的代码
- 方法 1 - 通过使用委托
MyTrainingViewControllerDelegate
and/or
- 方法 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
我被告知 我使用“tabbar”的方法实际上与使用“Singleton”相同,因为有一个共享数据源,多个视图正在访问该数据源。同样,我使用可通过多个视图控制器访问的视图模型的方法与使用“全局”变量的方法相同,这与使用“单例”具有相似的影响。
虽然这种方法在某些情况下可以接受,但它不是“最佳实践”,我需要更改我的一些 codes/approach,其中每个视图控制器将有自己的一组 data/view 模型。
背景
我有一个 workoutVM
视图模型,可以容纳我的大部分视图模型,我想将其传递给应用程序中的所有其他视图控制器。我的应用程序还有一个标签栏控制器,我用它来存储一些数据,比如用户信息等。
问题 1
即使我在 MyTrainingViewController
(“主页”选项卡)中创建了 workoutVM
视图模型(带值),我也无法将视图模型(带值)传递给接下来 ExerciseSetsViewController
(“锻炼”选项卡)使用
- 方法 1 - 使用委托
MyTrainingViewControllerDelegate
and/or - 方法 2 - 通过实例化
ExerciseSetsViewController
并加载视图。
当用户选择特定的表格视图单元格时,传递 workoutVM
视图模型的 codes/action 是 运行。
我真的不确定为什么其中一种方法有效,因为类似的方法适用于许多其他情况。下面是 Xcode 调试器,显示我使用这两种方法的代码没有将 workoutVM 视图模型传递给 ExerciseSetsViewController
问题2
因此,我找到了一个解决方法(方法 3 在下面的代码中被注释掉了) 利用 tabbar 来存储 workoutVM
视图模型(再次依靠标签栏在多个视图控制器之间传递和共享数据)。
在这一点上,恐怕我的应用实际上是将 tabbar 用作“单例”,尽管我“有点”明白它并不完全是“单例”。
我认为,理想情况下,视图模型应该充当某种数据模型,它们将 shared/manipulated/passed 跨多个视图控制器,而不需要将标签栏作为中间层。 这不对吗?或者这是我通过使用标签栏采用的 best/good 做法?
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
问题是我“通过故事板实例化”的 exerciseSetsViewController
与 TabbarController
的第二个选项卡(锻炼选项卡)中显示的 exerciseSetsViewController
不同.因此,workoutVM
未被传递以更正 viewcontroller。因此,如果我想使用
- 方法 1 - 通过使用委托
MyTrainingViewControllerDelegate
and/or - 方法 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
我被告知 我使用“tabbar”的方法实际上与使用“Singleton”相同,因为有一个共享数据源,多个视图正在访问该数据源。同样,我使用可通过多个视图控制器访问的视图模型的方法与使用“全局”变量的方法相同,这与使用“单例”具有相似的影响。
虽然这种方法在某些情况下可以接受,但它不是“最佳实践”,我需要更改我的一些 codes/approach,其中每个视图控制器将有自己的一组 data/view 模型。