【问题标题】:How to pass data properly when two VC is on same screen当两个VC在同一个屏幕上时如何正确传递数据
【发布时间】:2021-07-09 07:07:59
【问题描述】:

在我的应用中,它在同一屏幕下方有两个 ViewController。

  • ViewControllerA 是一个 UIViewController,其中包含一个 tableView。
  • ViewControllerB 是一个 containerView,它在 UITabBarController(root VC) 的顶部。

目前,我在viewWillAppearViewControllerA 中加载数据(歌曲数组、索引等),这将在后台队列中。同时ViewControllerBViewControllerA的数据加载完成后需要相同的数据。

目前,我正在使用依赖注入来保持唯一的数据源。在 ViewController A 和 B 中声明 modelController 引用,并使用 getter 和 setter 来绑定数据变化。

问题是从ViewControllerB 中的modelController 获取的歌曲数组为零。因为ViewControllerA会在加载完成时在modelController上设置数据。而ViewControllerB 将在其viewWillAppear 中的modelController 中使用它。但这两个VC在同一个屏幕上,我怎么能保证谁先走呢?数据加载也在后台队列中,所以它被认为是延迟的。那么如果ViewControllerB首先需要这些数据,那就不可能了。

我目前的想法是在ViewControllerB 上也加载数据,所以它不会依赖ViewControllerA 的结果。或者当数据准备好时添加一些观察者/听众到消息ViewControllerB。我认为这是一种建筑学的东西,我需要有经验的人的建议,任何提示都表示赞赏!

创建并注入 ModelController

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let _ = (scene as? UIWindowScene) else { return }
    
    guard let rootViewController = window?.rootViewController as? TabBarController else {
        fatalError("Unexpected Root View Controller")
    }
    
    // create dependency injection here
    rootViewController.modelController = ModelController()
    AudioManager.shared.modelController = rootViewController.modelController
}

模型控制器

enum PlayMode: String {
    case cycle = "repeat"
    case cycleOne = "repeat.1"
    case shuffle = "shuffle"
}

class ModelController {
    var songs: [Song]? = nil
    var position: Int = 0
    var playMode = PlayMode.cycle
}

Getter 和 Setter

var modelController: ModelController!

var songs: [Song]?
{
    get { modelController.songs }
    set { modelController.songs = newValue }
}

var position: Int
{
    get { modelController.position }
    set { modelController.position = newValue }
}

ViewControllerA中加载数据。

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(true)
        
    loadData()
}

func loadData() {
    self.showSpinner()
    songs?.removeAll()
    DispatchQueue.global(qos: .background).async { [weak self] in
        self?.songs = DataFetcher.shared.fetchMetaData().0
        DispatchQueue.main.async {
            print("data load complete")
            self?.table.reloadData()
            self?.removeSpinner()
        }
    }
}


【问题讨论】:

    标签: swift dependency-injection uiviewcontroller


    【解决方案1】:

    所以你说的也是可以的

    观察者/监听者在数据准备好时向 ViewControllerB 发送消息

    您可以拥有vcAvcB 都使用的共享数据类。例如

    class SharedData {
    
        static let shared = SharedData()
    
        // Use didLoad as a flag for both ViewControllers
        var didLoad: Bool {
           return self.data != nil
        }
    
        var data: SomeData!
    
        private init() {
          fetchData()
        }
    }
    

    在这个答案中有一个名为fetchData 的函数在同一个类中。您可以在后台调用fetchData 函数,并使用didLoaddata 作为标志来显示/调用您需要的任何其他函数。这个类也是一个singleton,这意味着在你的应用程序中会有一个这个类的实例——所以你可以在你的应用程序的任何地方使用它。

    这只是一个例子。您可以将其扩展到您的需要,例如添加一个委托以在加载数据时侦听回调,以便您可以在两个 VC 中执行某些操作。

    或者您可以简单地使用来自vcAprotocol(假设vcA 触发网络调用),一旦加载数据,您可以将其传递给vcB

    【讨论】:

    • 谢谢,我创建了一个 DataListener 协议,以便在数据加载完成时向该 ViewControllerB 广播消息。虽然我使用依赖注入而不是单例。好吧,我会重构我的项目,然后看看这种方法是否有效。
    猜你喜欢
    • 1970-01-01
    • 2017-10-21
    • 2020-03-15
    • 2021-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多