How to assign value to an outlet after delegating it? - ios

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?
Storyboard screenshot
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
}
}

-> Main VC (Where you need to get data)
class MainVC: UIViewController, ResultVCDelegate {
func passData(text: String) {
// label.text = text -- throws error
print(text)
}
#IBOutlet weak var label: UILabel!
#IBAction func change(_ sender: Any) {
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NavVC")
nextVC.delegate = self
present(nextVC, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
-> Result VC (from where you need to send data)
protocol ResultVCDelegate: AnyObject {
func passData(text: String)
}
class ResultVC: UIViewController {
weak var delegate: ResultVCDelegate?
var labelText = "Please Enter Something"
#IBOutlet weak var label: UILabel!
#IBAction func saveAndGoHome(_ sender: Any) {
self.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
}
}

Related

How to pass delegate thru Navtigation Controller?

I have 2 VC's, and one navigation controller between them. How can i set First screen as a delegate of Second?
What i tried:
Present SecondVC from frist (it presents it without navigation)
Setting delegate in NavVC viewDidLoad()
FirstVC:
class MainVC: UIViewController, SecondVCDelegate {
func passData(text: String) {
// do stuff
}
#IBAction func openNextVC(_ sender: Any) {
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NavVC") as! NavVC
present(nextVC, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Navigation Controller:
class NavVC: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
SecondVC:
protocol SecondVCDelegate {
func passData(text: String)
}
class SecondVC: UIViewController {
var delegate: SecondVCDelegate?
#IBAction func save(_ sender: Any) {
// do stuff
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Main task here would be to access second view controller instance from navigation controller instance(navC). And to achieve this, you have to first access the rootViewController from your navigation controller instance(nextVC) as below:
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NavVC") as! NavVC
let secondVC = nextVC?.viewControllers.first as? SecondVC
secondVC.delegate = self
present(nextVC, animated: true, completion: nil)
The above code is applicable when second view controller is the root of your navigation controller. If second view controller is not the root controller to navC, then you have to iterate the controllers to get second view controller instance. Once second controller instance is there, then you can assign the delegate to the same as below.
for controller in navC.viewControllers {
if controller.isKind(of: SecondVC.self) {
if let secondVc = controller as? SecondVC {
secondVc.delegate = self
}
break
}
}
I handled the same thing. Try this on MainVC:
class MainVC: UIViewController, SecondVCDelegate {
func passData(text: String) {
// do stuff
}
#IBAction func openNextVC(_ sender: Any) {
guard let secondController = self.storyboard?.instantiateViewController(withIdentifier: "AddGroupVC") as? AddGroupVC else { return }
secondController.delegate = { [weak self] in
print("call your delegates here")
}
self.navigationController?.present(secondController, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
On second View Controller, call the delegates in viewWillDisappear :
class SecondVC: UIViewController {
var delegate: SecondVCDelegate?
#IBAction func save(_ sender: Any) {
// do stuff
self.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillDisappear(_ animated: Bool) {
self.delegate
}
}

How to push data from textField to label in another VC

I have mainVC with 4 labels and 4 goToVC buttons, each button use segue to present another 1 of 4 VC. Every vc have textField and doneButton. I want to show text from textField in mainVC labels and i want to use delegates. But instead i got empty labels. Please help.
Delegate.swift
protocol TextFieldDelegate {
func didFinishTextField(text: String)
}
MainVC.swift
class MainViewController: UIViewController, TextFieldDelegate {
#IBOutlet weak var redText: UILabel!
#IBOutlet weak var orangeText: UILabel!
#IBOutlet weak var pinkText: UILabel!
#IBOutlet weak var purpleText: UILabel!
var choosenLabel: UILabel?
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toRedVC" {
let vc = segue.destination as! RedViewController
vc.delegate = self
choosenLabel = redText
} else if segue.identifier == "toOrangeVC" {
let vc = segue.destination as! OrangeViewController
vc.delegate = self
choosenLabel = orangeText
} else if segue.identifier == "toPinkVC" {
let vc = segue.destination as! PinkViewController
vc.delegate = self
choosenLabel = pinkText
} else if segue.identifier == "toPurpleVC" {
let vc = segue.destination as! PurpleViewController
vc.delegate = self
choosenLabel = purpleText
}
}
func didFinishTextField(text: String) {
if let data = choosenLabel {
data.text = text
}
}
#IBAction func redButton(_ sender: UIButton) {
}
#IBAction func orangeButton(_ sender: UIButton) {
}
#IBAction func pinkButton(_ sender: UIButton) {
}
#IBAction func purpleButton(_ sender: UIButton) {
}
}
RedVC(other 3 same).swift
class RedViewController: UIViewController {
var delegate: TextFieldDelegate?
var textVar: String = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func textField(_ sender: UITextField) {
if let data = sender.text {
textVar = data
}
}
#IBAction func doneButton(_ sender: UIButton) {
delegate?.didFinishTextField(text: textVar)
dismiss(animated: true, completion: nil)
}
}
Everything seems ok in your code. I suggest you to check your text field's actions are set to editingChanged in ViewController.

why in don't pass value user.username to navigator

class SigninViewController: UIViewController {
#IBOutlet weak var usernameTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
var signinAPIManager: SigninAPIManager?
override func viewDidLoad() {
super.viewDidLoad()
setupView()
setupData()
}
private func setupData() {
setupServices()
}
private func setupView() {
setupTextFields()
}
private func setupTextFields() {
let textFields = [usernameTextField, passwordTextField]
textFields.forEach{ $0?.delegate = self }
}
#IBAction func signinButton(_ sender: Any) {
signinAPIManager?.signin(optionalUsername: usernameTextField.text,
optionalPassword: passwordTextField.text)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "SigninCompletedIdentifier"{
if segue.destination is HomeTableViewController{
let vc = segue.destination as? HomeTableViewController
vc?.textUser = "asds"
}
}
}
}
}
extension SigninViewController: SigninAPIManagerDelegate {
func didSigninCompletion(user: User) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondController = storyboard.instantiateViewController(withIdentifier: "secondary") as! HomeTableViewController
secondController.textUser = user.username
performSegue(withIdentifier: "SigninCompletedIdentifier", sender: self)
}
}
I want to pass user.username in extension SigninViewController to navigator withIdentifier: "SigninCompletedIdentifier"
Please try to print your object first:
print("SignInApiManager Object= ", signinAPIManager)
You'll find it nil as you haven't assigned any instance of SigninAPIManager.
Replace the following line of code:
Replace var signinAPIManager: SigninAPIManager? with var signinAPIManager: SigninAPIManager = SigninAPIManager()

How to pass Data from the SearchBar to UILabel?

how do you pass data from search Bar to UILabel in a different view controller
Passing Data between View Controllers
I have tried multiple questions here on stack, but it just doesn't work or the simulator ends up crashing for me
class FirstVC: UIViewController, DataEnteredDelegate {
#IBOutlet weak var label: UILabel!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSecondViewController" {
let secondViewController = segue.destination as! SecondVC
secondViewController.delegate = self
}
}
func userDidEnterInformation(info: String) {
label.text = info
}
}
// protocol used for sending data back
protocol DataEnteredDelegate: class {
func userDidEnterInformation(info: String)
}
class SecondVC: UIViewController {
// making this a weak variable so that it won't create a strong reference cycle
weak var delegate: DataEnteredDelegate? = nil
#IBOutlet weak var searchBar: UISearchBar!
#IBAction func sendTextBackButton(sender: AnyObject) {
// call this method on whichever class implements our delegate protocol
delegate?.userDidEnterInformation(info: searchBar.text!)
// go back to the previous view controller
_ = self.navigationController?.popViewController(animated: true)
}
}
I don't find anything suspicious in your code which cause app crash. Meanwhile for data transfer between view controller we can use delegation and NotificationCenter. Below code uses NotificationCenter
let kStringTransfer = "StringTransferNotification"
class FirstViewController: UIViewController, DataEnteredDelegate {
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(setString(notify:)), name: NSNotification.Name(rawValue: kStringTransfer), object: nil)
}
func getSecondVC() {
if let second = self.storyboard?.instantiateViewController(withIdentifier: "Second") as? ViewController {
self.navigationController?.pushViewController(second, animated: true)
}
}
#IBAction func searchBarBtn(_ sender: Any) {
//go to search view controller
getSecondVC()
}
#objc func setString(notify: Notification) {
//set search bar string to text field
textField.text = notify.object as? String
}
}
class SecondViewController: UIViewController, UISearchBarDelegate {
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
searchBar.delegate = self
}
#IBAction func goBackBtn(_ sender: Any) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: kStringTransfer), object: searchBar.text)
self.navigationController?.popViewController(animated: true)
}
}
When I need pass just small values between 2 viewControllers, I don't use a delegate, just create a variable in second view and pass like this.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "showView") {
let vc = segue.destination as! secondViewController
vc.stringTemp = "My string"
}
}

Unwrapping the UILabel error while implementing Delegate and Protocols

Here I am getting error when unwrapping the del UIlabel
#IBOutlet weak var del: UILabel! // my label must change through protocol implementation
del.text = name // error here
Unexpectedly found nil while implicitly unwrapping an Optional value
ViewController.swift
class ViewController: UIViewController,DataPass {
#IBOutlet weak var del: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func seconeView(_ sender: UIButton) {
let selview = storyboard?.instantiateViewController(withIdentifier: "SecondViewController")as!SecondViewController
present(selview,animated: true,completion: nil)
}
func pass(name: String) {
del.text = name // error here Unexpectedly found nil while implicitly unwrapping an Optional value
}
}
SecondViewController.swift
protocol DataPass {
func pass(name:String)
}
class SecondViewController: UIViewController {
let delegate:DataPass! = ViewController()
let label = "Delegate"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func endView(_ sender: UIButton) {
if let del = delegate {
del.pass(name: label)
} else {
print("Delegate property is not set")
}
dismiss(animated: true, completion: nil)
}
}
There are few changes you need to do to make your delegate working:
In second controller make these changes:
protocol DataPass {
func pass(name:String)
}
class SecondViewController: UIViewController {
var delegate:DataPass?
let label = "Delegate"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func endView(_ sender: UIButton) {
if let del = delegate {
del.pass(name: label)
} else {
print("Delegate property is not set")
}
dismiss(animated: true, completion: nil)
}
}
and in first controller :
class ViewController: UIViewController,DataPass {
#IBOutlet weak var del: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func seconeView(_ sender: UIButton) {
let selview = storyboard?.instantiateViewController(withIdentifier: "SecondViewController")as!SecondViewController
selview.delegate = self
present(selview,animated: true,completion: nil)
}
func pass(name: String) {
del.text = name
}
}
Explanation:
In this line let delegate:DataPass! = ViewController() you are creating another instance for your delegate variable which doesn't hold any label. You need to assign your ViewController's instance while presenting the next controller to make it working.

Resources