简体   繁体   English

将数据传递给 TabBar 控制器

[英]Pass data to TabBar Controller

I want pass data between two ViewControllers throw the TabBarController.我想在两个 ViewControllers 之间传递数据抛出 TabBarController。 Inside the first ViewController I located textField and button.在第一个 ViewController 中,我找到了 textField 和按钮。 Inside the second I located Label.在第二个里面我找到了标签。 When I write some text in textField and push button, I expect that this text appear in the Label in the second ViewController.当我在 textField 和按钮中写入一些文本时,我希望该文本出现在第二个 ViewController 的 Label 中。 But nothing happens.但什么也没有发生。

And my code:还有我的代码:

First ViewController:第一个视图控制器:

import UIKit

class FirstViewController: UIViewController {
    @IBOutlet weak var textField: UITextField!

    @IBAction func enter(_ sender: Any) {
        if textField.text != "" {
            if let window = UIApplication.shared.delegate?.window, let tabBarController = window?.rootViewController as? UITabBarController, let second = tabBarController.viewControllers?.first as? SecondViewController {
                second.label.text = textField.text
                tabBarController.selectedIndex = 0
            }
        }
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

Second ViewController:第二个视图控制器:

import UIKit

class SecondViewController: UIViewController {

    @IBOutlet weak var label: UILabel!
    var myString = String()

    override func viewDidLoad() {
        super.viewDidLoad()

        label.text = myString
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

在此处输入图片说明

I think the problem is in我认为问题出在

tabBarController.viewControllers?.first as? SecondViewController

You probably want to do this instead:你可能想这样做:

tabBarController.viewControllers?[1] as? SecondViewController

Don't forget arrays are indexed from 0, so viewControllers?[1] actually returns the second element in the array.不要忘记数组从 0 开始索引,所以viewControllers?[1]实际上返回数组中的第二个元素。

First i think your view controllers that are representing tabs should be embedded into navigation controllers which would then be linked to the TabBarController.首先,我认为表示选项卡的视图控制器应该嵌入到导航控制器中,然后将其链接到 TabBarController。

Secondly, the preferred and recommended way to send data between controllers is through protocols (delegates).其次,在控制器之间发送数据的首选和推荐方式是通过协议(委托)。 Here is a good example you can check out, step by step: https://medium.com/@jamesrochabrun/implementing-delegates-in-swift-step-by-step-d3211cbac3ef这是一个很好的示例,您可以逐步查看: https : //medium.com/@jamesrochabrun/implementing-delegates-in-swift-step-by-step-d3211cbac3ef

However if you're looking for a quick fix of your solution i think that Bruno Phillipe's answer gets it to some extent, but not quite.但是,如果您正在寻找解决方案的快速解决方案,我认为 Bruno Phillipe 的答案在某种程度上可以解决问题,但并非完全如此。 We can't really be sure what controller is at what index in the view controller list.我们无法确定哪个控制器位于视图控制器列表中的哪个索引处。 I think this should work:我认为这应该有效:

@IBAction func enter(_ sender: Any) {
    if textField.text != "" {
        if let window = UIApplication.shared.delegate?.window, let tabBarController = window?.rootViewController as? UITabBarController {

            //check if there are view controllers in the tabBarController
            guard let vcList = tabBarController.viewControllers else {

                return
            }

            for controller in vcList {

                if let second = controller as? SecondViewController {
                    //this will be executed only when a controller is SeconfViewController
                    second.label.text = textField.text
                    tabBarController.selectedIndex = 0
                }
            }

        }
    }
}

EDIT:编辑:

I tried it myself and the problem is that you were trying to set the label.text when in fact the label component was never initialised.我自己尝试过,问题是您试图设置 label.text 而实际上标签组件从未初始化。 I think if you simply stored the textField value into myString variable in SecondViewController it would work (not sure).我认为如果您只是将 textField 值存储到 SecondViewController 中的 myString 变量中,它会起作用(不确定)。

However here's the solution using a protocol (delegate) which is the right way to send data between controllers.然而,这是使用协议(委托)的解决方案,这是在控制器之间发送数据的正确方法。 Ask any questions you might have.提出您可能有的任何问题。 This should work:这应该有效:

FirstViewController:第一个视图控制器:

import Foundation
import UIKit

protocol LabelChangeDelegate: class {

    func changeLabelWithText(_ text: String?)
}

class FirstViewController: UIViewController {

    weak var delegate: LabelChangeDelegate?

    @IBOutlet weak var textField: UITextField!

    @IBAction func enter(_ sender: UIButton) {

        if textField.text != "" {
            if let window = UIApplication.shared.delegate?.window, let tabBarController = window?.rootViewController as? UITabBarController {

                //check if there are view controllers in the tabBarController
                guard let vcList = tabBarController.viewControllers else {

                    return
                }

                for controller in vcList {

                    if let second = controller as? SecondViewController {
                        //this will be executed only when a controller is SeconfViewController

                        //set the delegate - who needs the data
                        delegate = second
                        //call the delegate function which will commmunicate with the delegate
                        delegate?.changeLabelWithText(textField.text!)
                        //don't know why you need this
                        tabBarController.selectedIndex = 0
                    }
                }

            }
        }
    }
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    }

    override func viewDidLoad() {
         super.viewDidLoad()
         // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
         super.didReceiveMemoryWarning()
         // Dispose of any resources that can be recreated.
    }


}

SecondViewController:第二视图控制器:

import Foundation
import UIKit

class SecondViewController: UIViewController, LabelChangeDelegate {

    @IBOutlet weak var label: UILabel!

    //lazy init
    lazy var myString = String()

    override func viewDidLoad() {
        super.viewDidLoad()

        //set label when the view loads, not in the first controller
        label.text = myString

        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
         super.didReceiveMemoryWarning()
         // Dispose of any resources that can be recreated.
    }

    //delegate function
    func changeLabelWithText(_ text: String?) {
        guard let sentText = text else {

            //no text sent
            return
        }

        myString = sentText
    }

}

I'm not recommending that you do it this way.我不建议你这样做。 This is an explanation for understanding.这是理解的解释。

  1. The viewControllers in a tabBarController know their tabBarController .在tabBarController的viewControllers知道他们tabBarController You can access it with self.tabBarController .您可以使用self.tabBarController访问它。
  2. The secondViewController is the second one in the list of viewControllers so let second = tabBarController.viewControllers?[1] as? SecondViewController secondViewControllerviewControllers列表中的第二个,所以let second = tabBarController.viewControllers?[1] as? SecondViewController viewControllers let second = tabBarController.viewControllers?[1] as? SecondViewController let second = tabBarController.viewControllers?[1] as? SecondViewController . let second = tabBarController.viewControllers?[1] as? SecondViewController
  3. If you haven't visited the secondViewController yet, its view will not have loaded, so the outlets will still be nil .如果你还没有访问secondViewController ,它的视图不会加载,所以出口仍然是nil You can force the view to load with _ = second.view .您可以使用_ = second.view强制加载视图。
  4. If you want to switch to the second tab, then you need to use tabBarController.selectedIndex = 1 .如果要切换到第二个选项卡,则需要使用tabBarController.selectedIndex = 1

@IBAction func enter(_ sender: Any) {
    if textField.text != "" {
        if let tabBarController = self.tabBarController as? UITabBarController, let second = tabBarController.viewControllers?[1] as? SecondViewController {
            // make sure view has loaded
            _ = second.view

            second.label.text = textField.text

            // change to second tab
            tabBarController.selectedIndex = 1
        }
    }
}

A better way...更好的方法...

Instead of setting the outlet directly, you should instead pass the string to a property of the SecondViewController :您应该将字符串传递给SecondViewController的属性,而不是直接设置插座:

second.myString = textField.text ?? ""

and then assign that string to the label in an override of viewWillAppear .然后将该字符串分配给viewWillAppear覆盖中的label

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    label.text = myString
}

The reason to set it in viewWillAppear is that viewWillAppear will run every time before the view is displayed.viewWillAppear设置它的原因是viewWillAppear将在每次显示视图之前运行。 viewDidLoad will only run once when the view is first loaded. viewDidLoad只会在第一次加载视图时运行一次。 Since you want the functionality to work multiple times, viewWillAppear is the correct override.由于您希望该功能多次工作,因此viewWillAppear是正确的覆盖。

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

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