Open different new view controllers by clicking different elements in table view cell - Swift 3 - uitableview

My table view cell displays an entity with two different button elements. I want to be able to launch a view controller that displays a selection of food items if I click on the first button and a different view controller that displays a selection of beverages when I click on the second button.
I am able to correctly pass the data to the new view controllers, but can't seem to dismiss the current view and load the new one. My code is like this:
In the table view cell
#IBAction func foodBtnPressed(_ sender: Any) {
print("foodBtn pressed")
print("customer is \(customer?.name)")
vc.loadChooserScreen(toChoose: "Food", forCustomer: customer!)
}
#IBAction func beverageBtnPressed(_ sender: UIButton) {
print("beverageBtn pressed")
print("customer is \(customer?.name)")
vc.loadChooserScreen(toChoose: "Beverage", forCustomer: customer!)
}
In the table view controller
func loadChooserScreen(toChoose: String, forCustomer: Customer) {
print("Choose \(toChoose)")
print("For \(forCustomer.name)")
if toChoose == "Food" {
let foodVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "foodMenu") as? FoodVC
foodVC?.loadCustomerToEdit(customer: forCustomer)
dismissVC(sender: Any.self)
}
else if toChoose == "Beverage" {
let beverageVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "beverageMenu") as? BeverageVC
beverageVC?.loadCustomerToEdit(customer: forCustomer)
dismissVC(sender: Any.self)
}
else {
// do nothing
}
}
func dismissVC(sender: Any) {
print("Reached dismissVC function in selectionMenu")
dismiss(animated: true, completion: {
self.delegate!.dismissViewController()
})
}
In this view controller I also have the following protocol
protocol OrderVCProtocol {
func dismissViewController()
}
and have defined
var delegate: OrderVCProtocol!
In my root view controller
func dismissViewController() {
print("Reached dismissViewController function in rootView")
if let foodVC = self.storyboard?.instantiateViewController(withIdentifier: "foodMenu") {
self.present(foodVC, animated: true, completion: nil)
}
if let beverageVC = self.storyboard?.instantiateViewController(withIdentifier: "beverageMenu") {
self.present(beverageVC, animated: true, completion: nil)
}
}
And the delegate is set when the table view controller is called here
#IBAction func loadOrderView(_ sender: Any) {
let orderVC = self.storyboard?.instantiateViewController(withIdentifier: "orderView") as! OrderVC
orderVC.delegate = self
self.present(orderVC, animated: true, completion: nil)
}
Within my target view controllers I have the following function
func loadCustomerToEdit(customer: Customer) {
self.customerToEdit = customer
print("IN FoodVC THE CUSTOMER TO EDIT IS \(self.customerToEdit.name)")
}
and a corresponding one in the BeverageVC.
When I run the app, no errors are thrown and I get the following sample output in the console from my print statements:
foodBtn pressed
customer is Optional("John")
Choose Food
For Optional("John")
IN FoodVC THE CUSTOMER TO EDIT IS Optional("John")
Reached dismissVC function in selectionMenu
and a corresponding response if the beverage button is clicked.
Then nothing happens. So I know the data is correctly being passed to the new view controllers but I don't know how to dismiss the current screen and display the new one with the choices.
I hope my question is clear enough? I'm not sure what's wrong, but the console output clearly shows that the code runs fine until it tries to dismiss the current view.
EDITED TO ADD:
If I modify my dismissVC function in my tableview controller like this:
func dismissVC(sender: Any) {
print("Reached dismissVC function in selectionMenu")
delegate.dismissViewController()
}
the console view now throws
fatal error: unexpectedly found nil while unwrapping an Optional value
And if I modify it again to the following, It goes back to throwing no errors and getting stuck at the same place (i.e. printing the line "Stuck where delegate dismisses view"), showing that the delegate is still nil... but why is it nil when I'd set it in the root view and loaded it in this view?
func dismissVC(sender: Any) {
print("Reached dismissVC function in selectionMenu")
if delegate != nil {
delegate?.dismissViewController()
} else {
print("Stuck where delegate dismisses view")
}

I have solved my problem by implementing notifications via notification centre and delegates. Firstly, in my AppDelegate file I added this line at the bottom
let notifyCnt = NotificationCenter.default
Next, I modified my tableview cell functions to this
#IBAction func foodBtnPressed(_ sender: Any) {
notifyCnt.post(name: NSNotification.Name(rawValue: "toChoose"), object: nil, userInfo: ["toChoose": "Food", "forCustomer": customer])
}
#IBAction func beverageBtnPressed(_ sender: UIButton) {
notifyCnt.post(name: NSNotification.Name(rawValue: "toChoose"), object: nil, userInfo: ["toChoose": "Beverage", "forCustomer": customer])
}
Then, in the tableview controller I modified it to this:
protocol ChooserViewDelegate: class {
func loadChooserView(choice: String, forCustomer: Customer)
}
and defined
weak var delegate: ChooserViewDelegate?
and added this within my ViewDidLoad section
notifyCnt.addObserver(forName: Notification.Name(rawValue: "toChoose"), object: nil, queue: nil, using: loadChooserScreen)
and finally modified my chooser function like so:
func loadChooserScreen(notification: Notification) {
guard let userInfo = notification.userInfo,
let toChoose = userInfo["toChoose"] as? String,
let planToEdit = userInfo["customer"] as? Customer else {
print("No userInfo found in notification")
return
}
delegate?.loadChooserView(choice: toChoose, forCustomer: customer)
}
Then in my root view controller I have the following to replace what I had earlier:
/*Conform to ChooserViewDelegate Protocol */
func loadChooserView(choice: String, forCustomer: Customer) {
self.customer = forCustomer
dismiss(animated: false, completion: {
if choice == "Food" {
self.performSegue(withIdentifier: "food", sender: self.customer)
}
if choice == "Beverage" {
self.performSegue(withIdentifier: "beverage", sender: self.customer)
}
})
}
and I send over the data via prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "food" {
if let foodVC = segue.destination as? FoodVC {
storyboard?.instantiateViewController(withIdentifier: "food")
foodVC.customerToEdit = self.customerToEdit
foodVC.delegate = self
}
}
if segue.identifier == "beverage" {
if let beverageVC = segue.destination as? BeverageVC {
storyboard?.instantiateViewController(withIdentifier: "beverage")
beverageVC.customerToEdit = self.customerToEdit
beverageVC.delegate = self
}
}
}
So now everything loads and views correctly :)

Related

Calling Barcodescanner from another class, cant retrieve the data? [duplicate]

I've followed the instructions here but I'm still unsure about this part:
modalVC.delegate=self;
self.presentViewController(modalVC, animated: true, completion: nil)
I've tried instantiating the view controller programmatically but still to no avail.
here's my code for when dismissing the modal view controller:
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
self.dismiss(animated: true) {
//
}
}
I'm using storyboard to segue with modal view.
This is the data I wish to transfer back to the parent view controller:
var typeState = "top"
var categoryState = "casual"
Which are two String values.
I've tried to pass data from the modal view controller as shown:
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
self.dismiss(animated: true, completion: nil)
delegate?.sendValue(value: "success")
if let presenter = presentingViewController as? OOTDListViewController {
presenter.receivedValue = "test"
}
}
whereas on the parent view controller I did as such:
func sendValue(value: NSString) {
receivedValue = value as String
}
#IBAction func printReceivedValue(_ sender: UIButton) {
print(receivedValue)
}
I still couldnt receive any value when I hit the print button.
Modal view controller:
protocol ModalViewControllerDelegate
{
func sendData(typeState: String, categoryState: String)
}
var delegate:ModalViewControllerDelegate!
var typeState = "top"
var categoryState = "casual"
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
self.dismiss(animated: true, completion: nil)
delegate?.sendData(typeState: typeState as String, categoryState: categoryState as String)
}
Parent view controller:
class parentViewController: UICollectionViewController, ModalViewControllerDelegate {
var typeState: String?
var categoryState: String?
func sendData(typeState: String, categoryState: String) {
self.typeState = typeState as String
self.categoryState = categoryState as String
}
#IBAction func printReceivedValue(_ sender: UIButton) {
print(typeState)
}
Here's my new code without using delegate method:
Modal view Controller:
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
self.dismiss(animated: true, completion: nil)
if let presenter = presentingViewController as? OOTDListViewController {
presenter.typeState = typeState
presenter.categoryState = categoryState
}
}
OOTDListViewController:
#IBAction func presentModalView(_ sender: UIBarButtonItem) {
let modalView = storyboard?.instantiateViewController(withIdentifier: "filterViewController") as! ModalViewController
let navModalView: UINavigationController = UINavigationController(rootViewController: modalView)
self.present(navModalView, animated: true, completion: nil)
}
#IBAction func printValue(_ sender: UIButton) {
print(typeState)
print(categoryState)
}
Depending on the data you want to pass, you can create a property in the presenting view controller, which you can set when dismissing the modal view controller, so you can spare yourself the delegate.
For example, you have a ContactsViewController, holding a var contacts: [Contact] = [] property. When you want to create a new contact, you present a modal view controller with the different values you need to create a new Contact object. When you are done and want to dismiss the view controller, you call the function as you did in your code, but set the property in the ContactsViewController. It will look something like this:
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
if let presenter = presentingViewController as? ContactsViewController {
presenter.contacts.append(newContact)
}
dismiss(animated: true, completion: nil)
}
If you don't want to use a delegate, this is how you go about it:
In your OOTDListViewController :
var testValue: String = ""
#IBAction func printReceivedValue(_ sender: UIButton) {
print(testValue)
}
In your modal view controller (I'll call it PresentedViewController) :
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
// if your OOTDListViewController is part of a UINavigationController stack, this check will probably fail.
// you need to put a breakpoint here and check if the presentingViewController is actually a UINavigationController.
// in that case, you will need to access the viewControllers variable and find your OOTDListViewController
if let presenter = presentingViewController as? OOTDListViewController {
presenter.testValue = "Test"
}
dismiss(animated: true, completion: nil)
}
If you want to use a delegate, this is how to do it:
In your OOTDListViewController:
protocol ModalDelegate {
func changeValue(value: String)
}
class OOTDListViewController: ModalDelegate {
var testValue: String = ""
#IBAction func presentViewController() {
// here, you either create a new instance of the ViewController by initializing it, or you instantiate it using a storyboard.
// for simplicity, I'll use the first way
// in any case, you cannot use a storyboard segue directly, bevause you need access to the reference of the presentedViewController object
let presentedVC = PresentedViewController()
presentedVC.delegate = self
present(presentedVC, animated: true, completion: nil)
}
func changeValue(value: String) {
testValue = value
print(testValue)
}
}
In your PresentedViewController:
class PresentedViewController {
var delegate: ModalDelegate?
var testValue: String = ""
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
if let delegate = self.delegate {
delegate.changeValue(testValue)
}
dismiss(animated: true, completion: nil)
}
}
If using a navigation controller you will have to first grab the UINavigation Controller and then get the correct ViewController from the Navigation Controller stack.
Here's how my code looked in that case.
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
if let navController = presentingViewController as? UINavigationController {
let presenter = navController.topViewController as! OOTDListViewController
presenter.testValue = "Test"
}
dismiss(animated: true, completion: nil)
}
I am using the a tab bar so the working code is as below:
if let tabBar = self.presentingViewController as? UITabBarController {
let homeNavigationViewController = tabBar.viewControllers![0] as? UINavigationController
let homeViewController = homeNavigationViewController?.topViewController as! HomeController
homeViewController._transferedLocationID = self.editingLocationID!
}
You need to call the delegate method in dismissViewController method
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
delegate?.sendData(typeState: "top", categoryState: "casual")
self.dismiss(animated: true) {
//
}
}
in you Modal ViewController class create delegate
weak var delegate: MyProtocol?
create a protocol with the method name sendData in MyProtocol and in your presentingViewController where you are assigning the delegate, implement the MyProtocol method
protocol MyProtocol: AnyObject {
func sendData(typeState: String, categoryState: String)
}
class ViewController: UIViewController, MyProtocol {
var typeState: String?
var categoryState: String?
override func viewDidApear() {
super.viewDidApear()
presentNewModalVC()
}
func presentNewModalVC() {
let modalVC = NewModalViewControllerToBePresented()
modalVC.delegate = self
present(modalVC, animated: true)
}
func sendData(typeState: String, categoryState: String) {
self.typeState = typeState
self.categoryState = categoryState
}
}

Array.append is overwriting not appending

I have two view controllers, one contains a table view with two sections where I display a list of movies, and another view controller where I can add movies. Assume the view controller that contains the table view is VC1 and the add movie is VC2.
The problem is when I add a movie in VC2 and append it to the list of movies in VC1, it just replaces the previously added movie.
I only add the movies locally during runtime. So whenever I run the app the list initially contains 0 movies. So I add a movies and then append it to the list. When I try to add another movie, it replaces the one before it as if the list cannot contain more than one item.
This is where I append the array:
#IBAction func addMovieButtonClicked(_ sender: UIButton) {
if imageView.image == nil || movieTitleField.text == "" || movieDateField.text == "" || movieOverviewField.text == "" {
displayAlert(title: "Warning", message: "Please enter all data to be able to add your movie!")
} else {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
let movie = Movie(title: movieTitleField.text!, overview: movieOverviewField.text!, date: movieDateField.text!)
vc.customMovies.append(movie)
vc.customImages.append(imageView)
self.navigationController?.pushViewController(vc, animated: true)
}
}
but when I move back to VC1 I find the data is replaced and not appended. Any solutions?
Edit:
The first view controller that contains the tableView that I show the data is called ViewController. The second one where I add a movie is called AddMovie.
This is how I navigate to the AddMovie:
#IBAction func addMovieButton(_ sender: UIBarButtonItem) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "AddMovie") as! AddMovie
self.navigationController?.pushViewController(vc, animated: true)
}
You are creating new instence every time ...
self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
You need to get the current and then append. To append movies you can use delegates as well as Closures
To use Delegate
protocol AddMoviesToController:AnyObject {
func addMoviesToController(movie:Movie , image:UIImage)
}
class AddMovie:UIViewController {
weak var delegate:AddMoviesToController?
#IBAction func addMovieButtonClicked(_ sender: UIButton) {
if imageView.image == nil || movieTitleField.text == "" || movieDateField.text == "" || movieOverviewField.text == "" {
displayAlert(title: "Warning", message: "Please enter all data to be able to add your movie!")
} else {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
let movie = Movie(title: movieTitleField.text!, overview: movieOverviewField.text!, date: movieDateField.text!)
delegate?.addMoviesToController(movie: movie, image: imageView)
self.navigationController?.pushViewController(vc, animated: true)
}
}
}
write extension like this to viewController
extension ViewController: AddMoviesToController {
func addMoviesToController(movie:Movie , image:UIImageView) {
//add movies here
customMovies.append(movie)
customImages.append(image)
tableView.reloadData()
}
}
And while pushing your controller
class ViewController: UIViewController {
#IBAction func addMovieButton(_ sender: UIBarButtonItem) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "AddMovie") as! AddMovie
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
}

How can I wait until a view controller is done presenting and dismissed to run next line of code? [duplicate]

I've followed the instructions here but I'm still unsure about this part:
modalVC.delegate=self;
self.presentViewController(modalVC, animated: true, completion: nil)
I've tried instantiating the view controller programmatically but still to no avail.
here's my code for when dismissing the modal view controller:
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
self.dismiss(animated: true) {
//
}
}
I'm using storyboard to segue with modal view.
This is the data I wish to transfer back to the parent view controller:
var typeState = "top"
var categoryState = "casual"
Which are two String values.
I've tried to pass data from the modal view controller as shown:
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
self.dismiss(animated: true, completion: nil)
delegate?.sendValue(value: "success")
if let presenter = presentingViewController as? OOTDListViewController {
presenter.receivedValue = "test"
}
}
whereas on the parent view controller I did as such:
func sendValue(value: NSString) {
receivedValue = value as String
}
#IBAction func printReceivedValue(_ sender: UIButton) {
print(receivedValue)
}
I still couldnt receive any value when I hit the print button.
Modal view controller:
protocol ModalViewControllerDelegate
{
func sendData(typeState: String, categoryState: String)
}
var delegate:ModalViewControllerDelegate!
var typeState = "top"
var categoryState = "casual"
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
self.dismiss(animated: true, completion: nil)
delegate?.sendData(typeState: typeState as String, categoryState: categoryState as String)
}
Parent view controller:
class parentViewController: UICollectionViewController, ModalViewControllerDelegate {
var typeState: String?
var categoryState: String?
func sendData(typeState: String, categoryState: String) {
self.typeState = typeState as String
self.categoryState = categoryState as String
}
#IBAction func printReceivedValue(_ sender: UIButton) {
print(typeState)
}
Here's my new code without using delegate method:
Modal view Controller:
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
self.dismiss(animated: true, completion: nil)
if let presenter = presentingViewController as? OOTDListViewController {
presenter.typeState = typeState
presenter.categoryState = categoryState
}
}
OOTDListViewController:
#IBAction func presentModalView(_ sender: UIBarButtonItem) {
let modalView = storyboard?.instantiateViewController(withIdentifier: "filterViewController") as! ModalViewController
let navModalView: UINavigationController = UINavigationController(rootViewController: modalView)
self.present(navModalView, animated: true, completion: nil)
}
#IBAction func printValue(_ sender: UIButton) {
print(typeState)
print(categoryState)
}
Depending on the data you want to pass, you can create a property in the presenting view controller, which you can set when dismissing the modal view controller, so you can spare yourself the delegate.
For example, you have a ContactsViewController, holding a var contacts: [Contact] = [] property. When you want to create a new contact, you present a modal view controller with the different values you need to create a new Contact object. When you are done and want to dismiss the view controller, you call the function as you did in your code, but set the property in the ContactsViewController. It will look something like this:
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
if let presenter = presentingViewController as? ContactsViewController {
presenter.contacts.append(newContact)
}
dismiss(animated: true, completion: nil)
}
If you don't want to use a delegate, this is how you go about it:
In your OOTDListViewController :
var testValue: String = ""
#IBAction func printReceivedValue(_ sender: UIButton) {
print(testValue)
}
In your modal view controller (I'll call it PresentedViewController) :
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
// if your OOTDListViewController is part of a UINavigationController stack, this check will probably fail.
// you need to put a breakpoint here and check if the presentingViewController is actually a UINavigationController.
// in that case, you will need to access the viewControllers variable and find your OOTDListViewController
if let presenter = presentingViewController as? OOTDListViewController {
presenter.testValue = "Test"
}
dismiss(animated: true, completion: nil)
}
If you want to use a delegate, this is how to do it:
In your OOTDListViewController:
protocol ModalDelegate {
func changeValue(value: String)
}
class OOTDListViewController: ModalDelegate {
var testValue: String = ""
#IBAction func presentViewController() {
// here, you either create a new instance of the ViewController by initializing it, or you instantiate it using a storyboard.
// for simplicity, I'll use the first way
// in any case, you cannot use a storyboard segue directly, bevause you need access to the reference of the presentedViewController object
let presentedVC = PresentedViewController()
presentedVC.delegate = self
present(presentedVC, animated: true, completion: nil)
}
func changeValue(value: String) {
testValue = value
print(testValue)
}
}
In your PresentedViewController:
class PresentedViewController {
var delegate: ModalDelegate?
var testValue: String = ""
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
if let delegate = self.delegate {
delegate.changeValue(testValue)
}
dismiss(animated: true, completion: nil)
}
}
If using a navigation controller you will have to first grab the UINavigation Controller and then get the correct ViewController from the Navigation Controller stack.
Here's how my code looked in that case.
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
if let navController = presentingViewController as? UINavigationController {
let presenter = navController.topViewController as! OOTDListViewController
presenter.testValue = "Test"
}
dismiss(animated: true, completion: nil)
}
I am using the a tab bar so the working code is as below:
if let tabBar = self.presentingViewController as? UITabBarController {
let homeNavigationViewController = tabBar.viewControllers![0] as? UINavigationController
let homeViewController = homeNavigationViewController?.topViewController as! HomeController
homeViewController._transferedLocationID = self.editingLocationID!
}
You need to call the delegate method in dismissViewController method
#IBAction func dismissViewController(_ sender: UIBarButtonItem) {
delegate?.sendData(typeState: "top", categoryState: "casual")
self.dismiss(animated: true) {
//
}
}
in you Modal ViewController class create delegate
weak var delegate: MyProtocol?
create a protocol with the method name sendData in MyProtocol and in your presentingViewController where you are assigning the delegate, implement the MyProtocol method
protocol MyProtocol: AnyObject {
func sendData(typeState: String, categoryState: String)
}
class ViewController: UIViewController, MyProtocol {
var typeState: String?
var categoryState: String?
override func viewDidApear() {
super.viewDidApear()
presentNewModalVC()
}
func presentNewModalVC() {
let modalVC = NewModalViewControllerToBePresented()
modalVC.delegate = self
present(modalVC, animated: true)
}
func sendData(typeState: String, categoryState: String) {
self.typeState = typeState
self.categoryState = categoryState
}
}

ViewController Pushing Swift From One VC to Another VC And Returning back

Consider two view controller Controller1 and Controller2, I have created a form of many UITextField in controller 1, in that when a user clicks a particular UITextField it moves to Controller2 and he selects the data there.
After selecting the data in Controller2 it automatically moves to Controller1, while returning from controller2 to controller1 other UITextfield data got cleared and only the selected data from controller2 is found. I need all the data to be found in the UITextfield after selecting.
Here is the code for returning from Controller2 to Controller1
if(Constants.SelectedComplexName != nil)
{
let storyBoard: UIStoryboard = UIStoryboard(name: "NewUserLogin", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "NewUser") as! NewUserRegistrationViewController
self.present(newViewController, animated: true, completion: nil)
}
To pass messages you need to implement Delegate.
protocol SecondViewControllerDelegate: NSObjectProtocol {
func didUpdateData(controller: SecondViewController, data: YourDataModel)
}
//This is your Data Model and suppose it contain 'name', 'email', 'phoneNumber'
class YourDataModel: NSObject {
var name: String? //
var phoneNumber: String?
var email: String?
}
class FirstViewController: UIViewController, SecondViewControllerDelegate {
var data: YourDataModel?
var nameTextField: UITextField?
var phoneNumberTextField: UITextField?
var emailTextField: UITextField?
override func viewDidLoad() {
super.viewDidLoad()
callWebApi()
}
func callWebApi() {
//After Success Fully Getting Data From Api
//Set this data to your global object and then call setDataToTextField()
//self.data = apiResponseData
self.setDataToTextField()
}
func setDataToTextField() {
self.nameTextField?.text = data?.name
self.phoneNumberTextField?.text = data?.phoneNumber
self.emailTextField?.text = data?.email
}
func openNextScreen() {
let vc2 = SecondViewController()//Or initialize it from storyboard.instantiate method
vc2.delegate = self//tell second vc to call didUpdateData of this class.
self.navigationController?.pushViewController(vc2, animated: true)
}
//This didUpdateData method will call automatically from second view controller when the data is change
func didUpdateData(controller: SecondViewController, data: YourDataModel) {
}
}
class SecondViewController: UIViewController {
var delegate: SecondViewControllerDelegate?
func setThisData(d: YourDataModel) {
self.navigationController?.popViewController(animated: true)
//Right After Going Back tell your previous screen that data is updated.
//To do this you need to call didUpdate method from the delegate object.
if let del = self.delegate {
del.didUpdateData(controller: self, data: d)
}
}
}
push your view controller instead of a present like this
if(Constants.SelectedComplexName != nil)
{
let storyBoard: UIStoryboard = UIStoryboard(name: "NewUserLogin", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "NewUser") as! NewUserRegistrationViewController
self.navigationController?.pushViewController(newViewController, animated: true)
}
and then pop after selecting your data from vc2 like this
self.navigationController?.popViewController(animated: true)
and if you are not using navigation controller then you can simply call Dismiss method
self.dismiss(animated: true) {
print("updaae your data")
}
There are a few ways to do it, but it usually depends on how you move from VC#1 to VC#2 and back.
(1) The code you posted implies you have a Storyboard with both view controllers. In this case create a segue from VC#1 to VC#2 and an "unwind" segue back. Both are fairly easy to do. The link provided in the comments does a good job of showing you, but, depending on (1) how much data you wish to pass back to VC#1 and (2) if you wish to execute a function on VC#2, you could also do this:
VC#1:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowVC2" {
if let vc = segue.destination as? VC2ViewController {
vc.VC1 = self
}
}
}
VC#2:
weak var VC1:VC1ViewController!
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if isMovingFromParentViewController {
VC1.executeSomeFunction()
}
}
Basically you are passing the entire instance of VC1 and therefore have access to everything that isn't marked private.
(2) If you are presenting/dismissing VC#2 from VC#1, use the delegate style as described by one of the answers.
VC#1:
var VC2 = VC2ViewController()
extension VC1ViewController: VC2ControlllerDelegate {
func showVC2() {
VC2.delegate = self
VC2.someData = someData
present(VC2, animated: true, completion: nil)
}
function somethingChanged(sender: VC2ViewController) {
// you'll find your data in sender.someData, do what you need
}
}
VC#2:
protocol VC2Delegate {
func somethingChanged(sender: VC2ViewController) {
delegate.somethingChanged(sender: self)
}
}
class DefineViewController: UIViewController {
var delegate:DefineVCDelegate! = nil
var someData:Any!
func dismissMe() {
delegate.somethingChanged(sender: self)
dismiss(animated: true, completion: nil)
}
}
}
Basically, you are making VC#1 be a delegate to VC2. I prefer the declaration syntax in VC#2 for `delegate because if you forget to set VC#1 to be a delegate for VC#2, you test will force an error at runtime.

Passing data from viewcontroler to tab viewed controllers in swift

I have this login form:
import UIKit
class LoginViewController: UIViewController {
var user : LoginUser?
#IBAction func btnLoginPressed(_ sender: Any) {
if self.textFieldLogin.text?.isEmpty ?? true || self.textFieldPassword.text?.isEmpty ?? true {
self.errorLoginMessage(txt: "Error", title: "Error")
} else {
let cms = ServerConnect()
cms.checkUsersLogin(login: self.textFieldLogin.text, password: self.textFieldPassword.text, completion: { (data) in
switch(data) {
case .succes(let data):
var actionToRun: ()-> Void
let decoder = JSONDecoder()
self.user = try? decoder.decode(LoginUser.self, from: data)
dump(self.user)
// we have an user
if ((self.user?.id ) != nil) {
actionToRun = {
self.performSegue(withIdentifier: "toLoginUser", sender: self)
}
}
// we have an error
else if let json = try? JSONSerialization.jsonObject(with: data, options: []),
let dictionary = json as? [String: Any],
let message = dictionary["komunikat"] as? String,
let title = dictionary["error"] as? String {
// we have an error
actionToRun = {
self.errorLoginMessage(txt: message, title: title)
}
}
// default error
else {
actionToRun = {
self.errorLoginMessage(txt: "Podany login lub hasło jest błędny!!", title: "Błąd")
}
}
DispatchQueue.main.async {
actionToRun()
}
case .error(let error):
print("Error 104: \(error)")
}
})
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if segue.identifier == "toLoginUser" {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let tabVC = storyboard.instantiateViewController(withIdentifier: "MainVC") as! UITabBarController
self.present(tabVC, animated: true, completion: {
let vc = tabVC.selectedViewController as! NewsViewController
vc.loggedUser = self.user
})
}
}
func errorLoginMessage(txt: String, title: String){
let alertController = UIAlertController(title: title, message: txt, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Ok", style: .cancel, handler: { (action: UIAlertAction!) in
}))
self.present(alertController, animated: true, completion: nil)
}
}
The object is built correctly.
I would like to pass my object from the login view to tab viewed controllers.
I am receiving the object like this:
class NewsViewController: UIViewController {
var loggedUser : LoginUser?
override func viewDidLoad() {
super.viewDidLoad()
print("check object: \(loggedUser)")
dump(loggedUser)
}
#IBAction func testBtnPressed(_ sender: Any) {
print("check object: \(loggedUser)")
dump(loggedUser)
}
}
Unfortunately this code does not work and I always get nil.
My full source code: https://bitbucket.org/trifek/karta-nauka/src/master/
Can I ask for help?
It is not good idea to pass info view controller to view controller which is used by almost all of your view controller's
Like Login information which contains info that is nearly used by all of the screen
Good idea is to create Global Shared class which will store your all global information which is shared among the application.
public final class AppGlobalManager {
static let sharedManager = AppGlobalManager()
var loggedUser : LoginUser?
}
Now whenever user login you can
AppGlobalManager.sharedManager.loggedUser = object
And when user logout
AppGlobalManager.sharedManager.loggedUser = nil
Hope it is helpful
If you are already using segues in the storyboard, you don't have to get the desired view controller from the UIStoryboard, the segues itself provides the destination view controller.
Example:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toLoginUser" {
let tabVC = segue.destination as! UITabBarController
// assuming that `NewsViewController` is the first view controller in the tabbar controller:
let destinationViewController = tabVC.viewControllers?[0] as! NewsViewController
destinationViewController.loggedUser = self.user
}
}
Since you are aiming to pass the LoginUser object to one of the tabbar view controllers, you could get it from the tabbar viewControllers array -as mentioned in the code snippet above-.

Resources