So my app has 3 screens: Main, Second, and Result (very simple)
On the Main screen, i show a label and button to change it
On the Second one I change label with textinput and pass it to Result (has
navigation controller)
On the Last screen I show the result and 2 buttons: save and cancel
My problem is I can't assign value to Main's outlet because it's nil, and I can't do anything with viewDidLoad() because it works only once when the app starts.
What can I do to fix this? is there any function to reload view so I can assign value in viewDidLoad?
The whole app is here: https://drive.google.com/file/d/1mvL2fVxjOHbL4dReCwJ8poIq9G9-ezny/view
MainVC:
class MainVC: UIViewController, ResultVCDelegate {
func passData(text: String) {
// label.text = text -- throws error
}
@IBOutlet weak var label: UILabel!
@IBAction func change(_ sender: Any) {
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NavVC")
present(nextVC, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
SecondVC:
class SecondVC: UIViewController {
@IBOutlet weak var inputText: UITextField!
@IBAction func save(_ sender: Any) {
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ResultVC") as! ResultVC
nextVC.labelText = inputText.text!
navigationController?.pushViewController(nextVC, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
ResultVC:
protocol ResultVCDelegate {
func passData(text: String)
}
class ResultVC: UIViewController {
var delegate: ResultVCDelegate?
var labelText = ""
@IBOutlet weak var label: UILabel!
@IBAction func saveAndGoHome(_ sender: Any) {
let mainVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MainVC") as! MainVC
self.delegate = mainVC
delegate?.passData(text: labelText)
dismiss(animated: true, completion: nil)
}
@IBAction func cancel(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
label.text = labelText.isEmpty ? label.text : labelText
}
}
BTW: I did similar app with two screens and it worked like a charm... strange
Following on from my comment.
Looking at your code, the issue is that you are not passing a reference to your MainVC to your ResultVC, and instead you are creating a new instance of MainVC, this results in a a crash because the view controller hasn't been properly created. But you don't want to create a new instance, as you need a reference to the original MainVC that you created.
You can get this by passing the reference, you will need to update all three of your ViewControllers. Something like this should work. Note I haven't tested this but this is the general principle.
class MainVC: UIViewController, ResultVCDelegate {
func passData(text: String) {
label.text = text
}
@IBOutlet weak var label: UILabel!
@IBAction func change(_ sender: Any) {
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NavVC")
nextVC.delegate = self // Here we add the delegate (the reference to MainVC)
present(nextVC, animated: true, completion: nil)
}
}
We need to add the delegate here and then forward it on to the ResultVC.
class SecondVC: UIViewController {
weak var delegate: ResultVCDelegate? // Add the delegate that will hold a reference to MainVC
@IBOutlet weak var inputText: UITextField!
@IBAction func save(_ sender: Any) {
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ResultVC") as! ResultVC
nextVC.labelText = inputText.text!
nextVC.delegate = delegate // Here we pass the reference to MainVC
navigationController?.pushViewController(nextVC, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
We can remove the code for instantiating the MainVC from the storyboard and instead just use the delegate to pass the value back to MainVC.
class ResultVC: UIViewController {
weak var delegate: ResultVCDelegate?
var labelText = ""
@IBOutlet weak var label: UILabel!
@IBAction func saveAndGoHome(_ sender: Any) {
// Just use the delegate no need to create a new instance
delegate?.passData(text: labelText)
dismiss(animated: true, completion: nil)
}
@IBAction func cancel(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
label.text = labelText.isEmpty ? label.text : labelText
}
}