[英]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.
这是理解的解释。
tabBarController
.tabBarController
。 You can access it with self.tabBarController
.self.tabBarController
访问它。secondViewController
is the second one in the list of viewControllers
so let second = tabBarController.viewControllers?[1] as? SecondViewController
secondViewController
是viewControllers
列表中的第二个,所以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
。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
强制加载视图。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.