简体   繁体   English

从Swift中的多个ViewControllers收集数据

[英]Collecting data from multiple ViewControllers in Swift

I'm having multiple viewcontrollers (in a navigation stack or maybe not) and each controllers collects some data based on users input. 我有多个viewcontrollers(在导航堆栈中或可能没有),每个控制器根据用户输入收集一些数据。 In the end I need to use those data in the last controller. 最后,我需要在最后一个控制器中使用这些数据。

So what would be the best approach/design-pattern to implement this scenario? 那么实现这种情况的最佳方法/设计模式是什么?

If you have (a navigation stack ) and you need to collect data from all this view controllers 如果您有(导航堆栈)并且需要从所有这些视图控制器收集数据

the best way Not to use User-defaults , or Singleton 最好的方法不使用User-defaults或Singleton

for your case you have to do container of view controllers to handle particular Process . 对于您的情况,您必须执行视图控制器容器来处理特定的进程。 Note ( UINavigationController , UITabBarController , and UISplitViewController * is just containers) 注意( UINavigationControllerUITabBarControllerUISplitViewController *只是容器)

For example create account that require 4 steps that collect users inputs , each step is represented by ViewController and at end you need to push all this data to API server , 例如,创建需要4个步骤来收集用户输入的帐户 ,每个步骤由ViewController表示,最后您需要将所有这些数据推送到API服务器,

So create Parent ContainerViewController of child viewControllers and use Delegation to pass data after every step to ParentViewController , ParentViewController will be leader to allow next step and provide Data for this step . 因此,创建子视图控制器的父ContainerViewController并使用Delegation在每个步骤之后将数据传递给ParentViewController ,ParentViewController将成为允许下一步的领导者并为此步骤提供数据。 Here is how to begin create Your own Container managing-view-controllers-with-container 以下是如何开始创建自己的容器管理视图控制器与容器

Do not use a Singleton 不要使用Singleton

Singletons can be accessed directly from anywhere in the app. 可以从应用程序的任何位置直接访问单身人士。 You have no control. 你无法控制。 a-lot coupling in your code and make your objects hard to test in the future 代码中的很多耦合,使您的对象将来很难测试

Do not use UserDefaults 不要使用UserDefaults

UserDefaults is to store user preferences that persist between app executions.Anything stored there will stay until you remove, so it is not a mechanism to pass data between objects. UserDefaults用于存储在应用程序执行之间持续存在的用户首选项。存储在那里的任何内容将一直保留到删除为止,因此它不是在对象之间传递数据的机制。

So you have to use architecture pattern in the future 所以你必须在将来使用架构模式

Architecture 建筑

Each ViewController should only take care of their own screen . 每个ViewController都应该只关注自己的屏幕。 also viewController shouldn't know about each other. viewController也不应该彼此了解。 if we do this we will remove a-lot of coupling between our view controller classes. 如果我们这样做,我们将删除视图控制器类之间的大量耦合。

So You can use Coordniator to be The leader that handle all navigation Coordniator 所以你可以使用Coordniator成为处理所有导航Coordniator的领导者

and check how to Integrate with MVVM at MVVM-C 并检查如何在MVVM-C上与MVVM集成

also Viper use this technique to handle navigation check Viper Viper也使用这种技术来处理导航检查Viper

Well, the best approach, in my opinion, is to use MVVM (Model-View-ViewModel) architecture pattern. 嗯,在我看来,最好的方法是使用MVVM(Model-View-ViewModel)架构模式。

1) For each of your UIViewControllers, create a separate class (derive from NSObject if you wish) that's you consider to be the "view model" belonging to that UIViewController. 1)对于每个UIViewControllers,创建一个单独的类(如果你愿意,从NSObject派生),你认为它是属于该UIViewController的“视图模型”。 Your view controllers are not allowed to access model classes... that's now a job of your "view model" classes. 您的视图控制器不允许访问模型类...现在这是您的“视图模型”类的工作。

2) Create a singleton named, say, DependencyManager. 2)创建一个名为DependencyManager的单例。 Your view models access it to obtain your models (or at least top-level models if they're hierarchial) and anything else they may need like networking services, etc... The DependencyManager acts as a way your unit tests can inject replacement "mock" versions of your models, network services, etc... when it's time to test each viewModel. 您的视图模型访问它以获取您的模型(或者至少是顶级模型,如果它们是分层的)以及它们可能需要的任何其他内容,例如网络服务等... DependencyManager充当您的单元测试可以注入替换的方式“模拟“模型的版本,网络服务等......在测试每个viewModel时。

3) Create your model classe(s) that contain the data. 3)创建包含数据的模型classe。 Your view controllers had collected various data within the UI controls, and given that raw data to your viewModels. 您的视图控制器已在UI控件中收集了各种数据,并将原始数据提供给viewModel。 The viewModels may mutate that data (or not) and stick it into the appropriate models they acquire from the DependencyManager. viewModels可以改变该数据(或不改变)并将其粘贴到他们从DependencyManager获取的适当模型中。

4) A ViewController is also allowed to ask their viewModel for data. 4)还允许ViewController向viewModel询问数据。 So your last ViewController would obtain whatever data it needs from its viewModel. 因此,您的上一个ViewController将从其viewModel获取所需的任何数据。

Remember: ViewControllers should not directly manipulate your model classes. 请记住:ViewControllers不应直接操作您的模型类。 Also, your ViewModels should never reference any UI objects, nor even have a reference to its ViewController. 此外,您的ViewModel永远不应该引用任何UI对象,甚至也不应该引用它的ViewController。

Side Note: I recommend having each ViewModel conform to a protocol which derives from an empty protocol named something like "MockableViewModelProtocol". 附注:我建议让每个ViewModel符合一个协议,该协议派生自名为“MockableViewModelProtocol”的空协议。 You can add a single property to your DependencyManager of type MockableViewModelProtocol that each of your ViewControllers can check before they create their ViewModel, in case a unit test has assigned a mock ViewModel for the ViewController to substitute. 您可以将单个属性添加到MockableViewModelProtocol类型的DependencyManager中,以便每个ViewControllers在创建ViewModel之前检查它们,以防单元测试为ViewController分配模拟ViewModel以进行替换。 Another benefit of such a protocol is for quick & clear understanding of the relationship between a ViewModel and its ViewController. 这种协议的另一个好处是可以快速清楚地理解ViewModel与其ViewController之间的关系。 Often you'll have not just properties and methods but also callback properties (closure properties). 通常,您不仅具有属性和方法,还具有回调属性(闭包属性)。

So there ya go. 所以,你去。 That, in my opinion, is the best way to not only design a way to manage and access data among a bunch of viewControllers, but to also test all your classes and their use of that data. 在我看来,这不仅是设计一种在一组viewControllers中管理和访问数据的方法,而且还是测试所有类及其对数据的使用的最佳方法。

And unlike all the current claims that Storyboards block dependency injection and thus prevent testing of your ViewControllers, that's just bunk. 并且与目前声称Storyboards阻止依赖注入并因此阻止对ViewControllers进行测试的说法不同,这只是下铺。 By using a DependencyManager, such that your view controller tests inject mocks there, the tests get the same benefit as if they injected a mock directly into the ViewController, and yet the ViewController is still instantiated by the Storyboard. 通过使用DependencyManager,您的视图控制器测试在那里注入模拟,测试获得了与将模拟直接注入ViewController相同的好处,但ViewController仍然由Storyboard实例化。 I've shipped a large app quite successfully with this approach. 我用这种方法非常成功地发布了一个大型应用程序。

There can be multiple ways to achieve that, 可以有多种方法来实现这一目标,

  1. Pass the data along the controllers until the last one where you want to use it. 沿控制器传递数据 ,直到最后一个要使用它的数据。

  2. Use a Singleton to store the data from each controller. 使用Singleton存储来自每个控制器的数据。 The only issue with Singleton is that it will persist in your app. Singleton的唯一问题是它会在你的应用程序中持久存在。

  3. You can also store the data in UserDefaults . 您还可以将数据存储在UserDefaults After using the data, you can clear it out from UserDefaults . 使用数据后,您可以从UserDefaults清除它。

Let me know if you still face any issues. 如果您仍然遇到任何问题,请告诉我。

In my opinion the best way to handle this would be to use a Flux design pattern. 在我看来,处理这个的最好方法是使用Flux设计模式。 There is a framework called ReSwift on github that allows for unidirectional data flow similar to Redux . 在github上有一个名为ReSwift的框架,它允许类似于Redux的单向数据流。

Even if you do not use this framework specifically, the concept of unidirectional data flow is perfect for this situation. 即使您没有专门使用此框架,单向数据流的概念也适用于这种情况。 The state gathered from each view controller will exist outside of any view controllers, the view controllers will stay agnostic of each other, and there will be no need for any parent view controllers to act has higher order coordinators. 从每个视图控制器收集的状态将存在于任何视图控制器之外,视图控制器将保持彼此不可知,并且不需要任何父视图控制器具有更高级别的协调器。 Then on your last view controller, you access the state and use it however you need. 然后在最后一个视图控制器上,您可以访问该状态并根据需要使用它。

Another benefit is if you need to break out your view controllers into multiple view controllers through containers, ie one screen has 2+ view controllers, you will not need to create more infrastructure to pass data between those, which in my experience gets really messy. 另一个好处是,如果您需要通过容器将视图控制器分成多个视图控制器,即一个屏幕有2个以上的视图控制器,您将不需要创建更多的基础设施来在这些控制器之间传递数据,这在我的经验中变得非常混乱。

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

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