简体   繁体   English

从 UIViewController 获取数据到另一个 UIViewController

[英]Get Data from UIViewController to Another UIViewController

Suppose I have a storyboard like so:假设我有一个像这样的故事板:

故事板图片

Is it possible for me to get a flag or a boolean data from A back to B?我是否有可能将标志或布尔数据从 A 返回到 B? I initially thought of using delegation but most of the tutorials about it talks about sending data between UIViewControllers that are part of 1 NavigationController .我最初想到使用委托,但大多数关于它的教程都讨论了在属于 1 NavigationController UIViewControllers之间发送数据。 In my case, the UIViewController I need to get data is outside of the navigation controller.就我而言,我需要获取数据的UIViewController位于导航控制器之外。 Is there a way for me to send data from A to B despite not being embedded in the same NavigationController ?尽管没有嵌入到同一个NavigationController中,有没有办法让我将数据从 A 发送到 B ?

If you don't want to use delegate between the classes .如果您不想在类之间使用委托。 One possible way is to create separated file , saved in class and fetch required data any where in navigation .一种可能的方法是创建单独的文件,保存在类中并在导航中的任何位置获取所需的数据。

Useful class for your case would be create singleton class FlowEngine .对您的案例有用的类是创建单例类FlowEngine Use getter / setter method for saving and fetching of data.使用getter / setter方法来保存和获取数据。 Code is attached for your reference .附上代码供您参考。

class FlowEngine : NSObject{

    private let static shared =  FlowEngine()

    private var data : String

    private init(){

    }

    func savedData(text : String){
       data  = text
    }

    func fetchSavedData() -> String{

      return data  // add checsk for nil values
    }

}

if your A viewController is not huge, In B viewController do this :如果您的 A viewController 不是很大,则在 B viewController 中执行以下操作:

class B : UIViewController {

  var a : A! = nil


    func viewDidLoad() {
      super.viewDidLoad()


    a = storyboard?.instantiateViewController(withIdentifier: "StoryBoard ID") as? A
    if a.booleanValue == true { 

        // use your booleanValue
       a = nil // deallocate after using your value.
   }

}


 }

Delegation doesn't require the ViewControllers to be in same navigation stack.委派不需要 ViewControllers 位于同一个导航堆栈中。 You can use the same for your case.您可以将其用于您的案例。 However, if you choose to go with NotificationCenter, just remember to remove the observer when appropriate.但是,如果您选择使用 NotificationCenter,请记住在适当的时候移除观察者。

Other answers seem to accomplish your requirements but for the sake of completeness you could try to use KVC and KVO for modifying values in A and receiving its changes in B (or any other place)其他答案似乎可以满足您的要求,但为了完整起见,您可以尝试使用KVCKVO来修改A值并接收其在B (或任何其他地方)中的更改

You could see a detailed explanation of how to use them in here .您可以在此处查看有关如何使用它们的详细说明。

You have several ways to go, depending on your needs :根据您的需要,您有多种方法可以选择:

  1. Delegation代表团

Declare a protocol in A, and make B conform to it.在 A 中声明一个协议,并使 B 遵守它。 Set the delegate of A to B. This could be cumbersome if the navigation stack has too many level, as you would need to pass the reference of B to each ViewController between A & B将 A 的委托设置为 B。如果导航堆栈的级别太多,这可能会很麻烦,因为您需要将 B 的引用传递给 A 和 B 之间的每个 ViewController

  1. Notification / KVO通知/KVO

B subscribe to a notification sent by A, no reference needed, thread safe. B 订阅 A 发送的通知,不需要引用,线程安全。 Don't forget to unsubscribe when done.完成后不要忘记取消订阅。

  1. Proxy class代理类

Use a proxy singleton class, that will hold your data.使用代理单例类,它将保存您的数据。 A will write to it, and B will read it in viewWillAppear . A 将写入它,B 将在viewWillAppear读取它。

  1. UserDefaults用户默认值

Same concept as a Proxy Class, but the data will persist during your app life cycle and even after killing the app.与代理类的概念相同,但数据将在您的应用程序生命周期中持续存在,甚至在杀死应用程序之后也是如此。 It's appropriate if you want to change a flag or a setting for your user, not if you have a lot of data to hold.如果您想为您的用户更改标志或设置,这是合适的,而不是如果您要保存大量数据。

Cocoa Touch uses the target-action mechanism for communication between a control and another object. Cocoa Touch 使用目标-动作机制在控件和另一个对象之间进行通信。 More here... If you would like to use it with UIControl objects like buttons, then you can set it in Interface Builder by sending an action to the FirstResponder object. 更多信息...如果您想将它与按钮等 UIControl 对象一起使用,那么您可以通过向 FirstResponder 对象发送一个操作来在 Interface Builder 中设置它。

Target-Action will start searching a VC which responds to a given method from the current first responder and then will move to the next responder and will terminate a search in a current UIWindow. Target-Action 将开始搜索一个 VC,该 VC 响应来自当前第一响应者的给定方法,然后将移动到下一个响应者并终止在当前 UIWindow 中的搜索。 Once a controller which responds to a method signature is found, the search is terminated.一旦找到响应方法签名的控制器,搜索就会终止。

class AViewController: UIViewController {

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


    @IBAction func configure(with dictionary: Dictionary<String, Any>) {
        print(dictionary)
    }
}

class BViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let a = self.targetViewController(forAction: #selector(ViewController.configure(with:)), sender: self) as? ViewController
        a?.configure(with: ["firstName": "Alex", "lastName": "Toto"])

    }
}

Update (better solution)更新(更好的解决方案)

We've had to edit a few things to the functionality which presented me with the opportunity to refactor this.我们不得不编辑一些功能,这让我有机会重构它。 I used the NSNotification way, which was way cleaner than using closures.我使用了NSNotification方式,这比使用闭包更干净。

ViewControllerB视图控制器B

override func viewDidLoad() {
    super.viewDidLoad()
    //Observe for notification from "myIdentifier"
    NotificationCenter.default.addObserver(self, selector: #selector(self.processNotification(notification:)), name: Notification.Name("myIdentifier"), object: nil)
}

//function that gets called when notification is received
//the @objc annotation is required!
@objc func processNotification(notification: Notification) {
    //Do something
}

ViewControllerA视图控制器A

@IBAction func didTapButton(_ sender: Any) {
    //Process something
    // ...
    //

    //Post a notification to those observing "myIdentifier"
    NotificationCenter.default.post(name: Notification.Name("myIdentifier"), object: nil)
    self.dismiss(animated: true, completion: nil)
}

Old (but working) solution旧(但有效)的解决方案

This might be an unpopular solution but I managed to solve this with callbacks.这可能是一个不受欢迎的解决方案,但我设法通过回调解决了这个问题。 I was looking into another possible solution which was commented NSNotification but since someone from the team already had experience with using callbacks in this manner, we decided to ultimately use that.我正在研究另一个可能的解决方案,它被评论为NSNotification但由于团队中的某个人已经有以这种方式使用回调的经验,我们决定最终使用它。

How we made it work:我们如何使它工作:

ViewControllerB is given the actual code implementation through prepare(for segue: UIStoryboardSegue, sender: Any?) while ViewControllerC (This is the middle UIViewController in the picture) has a callback property and ViewControllerA contains the value to pass when it's about to be dismissed. ViewControllerB通过prepare(for segue: UIStoryboardSegue, sender: Any?)给出了实际的代码实现prepare(for segue: UIStoryboardSegue, sender: Any?)ViewControllerC (这是图中中间的UIViewController )有一个回调属性, ViewControllerA包含要在它即将被解除时传递的值。

ViewControllerB视图控制器B

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
   if segue.identifier == "secondSegue" {
      let nvc: NavigationController = segue.destination as! NavigationController
      let vc = nvc.viewControllers[0] as! ViewControllerC
      vc.completion = { hasAgreed in 
         //Do Something
      }
   }
} 

ViewControllerC视图控制器C

class ViewControllerC: UIViewController {
   var completion: ((Bool) -> ())?

   override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
   if segue.identifier == "thirdSegue" {
      let nvc: NavigationController = segue.destination as! NavigationController
      let vc = nvc.viewControllers[1] as! ViewControllerA
      vc.middleController = self
   }
} 

ViewControllerA视图控制器A

class ViewControllerC: UIViewController {
   var middleController: ViewControllerC?

   @IBAction func didTapButton(_ sender: Any) {
      self.dismiss(animated: true, completion: {
         middleController?.completion(true)
      }) 
   }
} 

With this, we got the data we needed from the diagram picture above.有了这个,我们从上面的图表中得到了我们需要的数据。

Your best bet is to make use of NotificationCenter to achieve this.最好的办法是利用NotificationCenter来实现这一点。

Post notification like this:像这样发布通知:

NotificationCenter.default.post(name: Notification.Name("NotificationName"), object: nil, userInfo: ["somekey":"somevalue"])

Observe it like this:像这样观察它:

NotificationCenter.default.addObserver(self, selector: #selector(self.dataReceived(notification:)), name: Notification.Name("NotificationName"), object: nil)

Use the following method:使用以下方法:

@objc func dataReceived(notification: Notification) {}

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

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