Swift, Pass data back from popover to view controller - swift

I have sorted the original array in popover controller. Now I want to send that array back to the original view controller for tableview and map view.
Below is my code
If propertyNameSrt == false
{
if ascSorting == false
{
properties.sort(sorterForbuildingAsc)
}
else
{
properties.sort(sorterForbuildingDesc)
}
}
My array is properties which includes custom object.
How can pass this to my original view controller?
Thanks in advance,
Dhaval.

You can use delegate(protocol) methods to send back data to previous view controller.
IN CURRENT VC:
protocol MyProtocol: class
{
func sendArrayToPreviousVC(myArray:[AnyObject])
}
Make a var in your class.
weak var mDelegate:MyProtocol?
Now call the protocol method when you pop the view controller, with your "properties" array as parameter.
mDelegate?.sendArrayToPreviousVC(properties)
IN PREVIOUS VC:
In your previous VC, set the mDelegate property to self, when you push the current VC.
currentVC.mDelegate = self
//PUSH VC
Now implement the protocol method in your previous VC.
func sendArrayToPreviousVC(myArray:[AnyObject]) {
//DO YOUR THING
}

Related

How to access a referencing outlet from a different view controller?

I am new to Swift and Xcode. I am building an Financial Expense ios app.
In my first view controller, I created a referencing outlet for a label called expenseNum.
In my second view controller, I have a function for a button called Add Expense. When it is clicked, I need it to update the expenseNum variable with the amount of the expense.
What is the best way to go about this? I had created an object of the first view controller class and accessed it like "firstviewcontroller.expenseNum" but this will create a new instance of the class and I need it to be all the same instance so it can continuously add to the same variable. Thanks for the help!
You need a delegate
protocol SendManager {
func send(str:String)
}
In first
class FirstVc:UIViewcontroller , SendManager {
func send(str:string) {
self.expenseNum.text = str
}
}
when you present SecondVc
let sec = SecondVc()
sec.delegate = self
// present
In second
class SecondVc:UIViewcontroller {
var delegate:SendManager?
#IBAction func btnClicked(_ sender:UIButton) {
delegate?.send(str:"value")
}
}
// setting delegate
in viewDidLoad of SecondVc
if let first = self.tabBarController.viewControllers[0] as? FirstVc {
self.delegate = first
}
There are several ways you can pass data from ViewController2 to another ViewController1
The best way here is Protocol Delegates
Please follow below steps to pass data
In Your SecondViewController from where you want to send data back declare a protocol at the top of class declaration
protocol SendDataBack: class {
func sendDataFromSecondVCtoFirstVC(myValue: String)
}
Now in the class , declare a object of your protocol in same ViewController
weak var myDelegateObj: SendDataBack?
And now in your Add Expense button action just call the delegate method
myDelegateObj?.sendDataFromSecondVCtoFirstVC(myValue: yourValue)
Now go to your first ViewController
the place from where you have pushed/present to SecondViewController you must have taken the object of SecondVC to push to push from first
if let secondVC = (UIStoryboard.init(name: "Main", bundle: nil)).instantiateViewController(withIdentifier: "secondVCID") as? SecondViewController {
vc?.myDelegateObj = self
self.navigationController?.pushViewController(secondVC, animated: true)
**OR**
self.present(secondVC, animated: true, completion: nil)
}
now in your FirstViewController make an extension of FirstViewVC
extension FirstViewController: SendDataBack {
func sendDataFromSecondVCtoFirstVC(myValue: String) {
}
}
I think you can make a variable in your properties in second ViewController (before viewDidLoad method)
var delegate: FirstViewController? = nil
and use from the properties of the first view controller anywhere of the second view controller.
delegate!.mainTableView.alpha=1.0
//for example access to a tableView in first view controller
The simplest way to achieve this is to use a public var. Add a new Swift file to your project, call it Globals. Declare the public variable in Globals.swift like so:
public var theValue: Int = 0
Set its required value in the first ViewController, and you'll find you can read it in the second with ease.

Destination of navigation back button swift

I connected UITableViews with Show segue. I want to change some variable of destination tableView whenever I tap the Back button on navigation bar. Let's say I'm not allowed to create a new back button, how can I detect/call the destination tableView when the back button is tapped? Like in prepareforsegue we have segue.destination, is there some thing like "backbutton.destination"?
Thanks!
What you should be doing here (I think) is not trying to find the destination of the back button but passing in the interested object when going forwards.
On you detail view you could have something like...
protocol DetailViewDelegate {
func detailViewDidChange()
}
Then in the detail view controller...
class DetailViewController: UIViewController {
weak var delegate: DetailViewDelegate?
// you could call this from viewWillDisappear or something
func doThisWhenYouWantToUpdateTheTableView() {
delegate?.detailViewDidChange()
}
}
Now in the tableview...
func prepareForSegue(...) {
if let vc = segue.destination as? DetailViewController {
vc.delegate = self
}
}
and...
extension MyTableViewController: DetailViewDelegate {
func detailViewDidChange() {
// respond to the change here.
}
}

(re)-Pass data after click on backbutton

I'm trying to pass data from a SecondViewController to my FirstViewController when I click on my back button (UINaviagtionController).
For pass my data from FirstViewController to the SecondViewController I do this:
if segue.identifier == "showSecondVC" {
let vc = segue.destination as! SecondViewController
vc.rows = rows[pathForAVC]
vc.lap = lapTime[pathForAVC]
vc.indexPath = pathForAVC
}
But I have no idea how to pass data from SecondViewController to the FirstViewController and I really don't understand topics about it on Stack Overflow.
I want to transfer it when I click here:
Thanks.
You can use delegate pattern for that. You can grab the back button press event like this and update the data
override func viewWillDisappear(_ animated: Bool) {
if self.isMovingFromParentViewController {
self.delegate.updateData( data)
}
}
For more information on delegates you can go through this.
Actually things depend on your requirement, if you want data to be updated in first view controller as soon as it is updated in second view controller, you would need to call delegate as soon as the data is updated. But as in the question you have mentioned that you want it to be updated on back button only, above is the place to do it.
Another way would be to have Datasource as singleton so that it is available to all the view controllers and the changes are reflected in all view controllers. But create singleton if absolutely necessary, because these nasty guys hang around for entire time your application is running.
You should have a custom protocol such as:
public protocol SendDataDelegate: class {
func sendData(_ dataArray:[String])
}
Here I suppose you want to send a single array back to FirstViewController
Then make your first view controller to conform to the custom protocol, such as:
class FirstViewController: UIViewController, SendDataDelegate
In the second view controller, create a delegate a variable for that protocol, such as:
weak var delegate: SendDataDelegate?
and then you catch the back action and inside it you call your custom protocol function, such as:
self.delegate?.sendData(arrayToSend)
In the first viewController, in the prepare for segue function just set the delegate like
vc.delegate = self

Access the presenting view controller from the presented view controller?

I have a view controller (containing my menu) presented on top of another view controller (my application).
I would need to access the presenting view controller (below my menu) from the presented view controller (my menu), for example to access some variables or make the presenting view controller perform one of its segues.
However, I just can't figure out how to do it.
I'm aware of the "presentingViewController" and "presentedViewController" variables but I didn't manage to use them successfully.
Any Idea ?
Code (from the presented VC, which as a reference to the AppDelegate in which the window is referenced) :
if let presentingViewController = self.appDelegate.window?.rootViewController?.presentingViewController {
presentingViewController.performSegue(withIdentifier: "nameOfMySegue", sender: self)
}
Here is a use of the delegation Design pattern to talk back to the presenting view controller.
First Declare a protocol, that list out all the variables and methods a delegate is expected to respond to.
protocol SomeProtocol {
var someVariable : String {get set}
func doSomething()
}
Next : Make your presenting view controller conform to the protocol.
Set your presenting VC as the delegate
class MainVC: UIViewController, SomeProtocol {
var someVariable: String = ""
func doSomething() {
// Implementation of do
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Your code goes here.
if let destVC = segue.destination as? SubVC{
destVC.delegate = self
}
}
}
Finally, when you are ready to call a method on the presenting VC (Delegate).
class SubVC: UIViewController {
var delegate : SomeProtocol?
func whenSomeEventHappens() {
// For eg : When a menu item is selected
// Set some Variable
delegate?.someVariable = "Some Value"
// Call a method on the deleate
delegate?.doSomething()
}
}
Assuming that VCApplication is presenting VCMenu, in VCMenu you can access VCApplication with:
weak let vcApplication = self.presentingViewController as? VCApplicationType
Your example self.appDelegate.window?.rootViewController?.presentingViewController is looking for the ViewController that presented the rootViewController - it will be nil.
EDIT
Per TheAppMentor I've added weak so there are no retain cycles.

Swift – Using popViewController and passing data to the ViewController you're returning to

I have an optional bool variable called showSettings on my first view controller which is called ViewController, and I'm popping from SecondViewController back to ViewController.
Before I pop, I want to set the bool to true. Seems wrong to instantiate another view controller since ViewController is in memory.
What's the best way to do this? I'm not using storyboards, if that's important for your answer.
Thanks for your help
So I figured it out, based mostly from this post – http://makeapppie.com/2014/09/15/swift-swift-programmatic-navigation-view-controllers-in-swift/
In SecondViewController, above the class declaration, add this code:
protocol SecondVCDelegate {
func didFinishSecondVC(controller: SecondViewController)
}
Then inside of SecondViewContoller add a class variable:
var delegate: MeditationVCDelegate! = nil
Then inside of your function that your button targets, add this:
self.navigationController?.popViewControllerAnimated(true)
delegate.didFinishSecondVC(self)
What we're doing here is doing the pop in SecondViewController, and not passing any data, but since we've defined a protocol, we're going to use that in ViewController to handle the data.
So next, in ViewController, add the protocol you defined in SecondViewController to the list of classes ViewController inherits from:
class ViewController: UIViewController, SecondVCDelegate { ... your code... }
You'll need to add the function we defined in the new protocol in order to make the compiler happy. Inside of ViewController's class, add this:
func didFinishSecondVC(controller: SecondViewController) {
self.myBoolVar = true
controller.navigationController?.popViewControllerAnimated(true)
}
In SecondViewController where we're calling didFinishSecondVC, we're calling this method inside of the ViewController class, the controller we're popping to. It's similar to if we wrote this code inside of SecondViewController but we've written it inside of ViewController and we're using a delegate to manage the messaging between the two.
Finally, in ViewController, in the function we're targeting to push to SecondViewController, add this code:
let secondVC = secondViewController()
secondVC.delegate = self
self.navigationController?.pushViewController(secondVC, animated: true)
That's it! You should be all set to pass code between two view controllers without using storyboards!
_ = self.navigationController?.popViewController(animated: true)
let previousViewController = self.navigationController?.viewControllers.last as! PreviousViewController
previousViewController.PropertyOrMethod
I came across this while looking for a way to do it. Since I use Storyboards more often, I found that I can get the array of controllers in the navigation stack, get the one just before the current one that's on top, check to see if it's my delegate, and if so, cast it as the delegate, set my methods, then pop myself from the stack. Although the code is in ObjC, it should be easily translatable to swift:
// we need to get the previous view controller
NSArray *array = self.navigationController.viewControllers;
if ( array.count > 1) {
UIViewController *controller = [array objectAtIndex:(array.count - 2)];
if ( [controller conformsToProtocol:#protocol(GenreSelectionDelegate)]) {
id<GenreSelectionDelegate> genreDelegate = (id<GenreSelectionDelegate>)controller;
[genreDelegate setGenre:_selectedGenre];
}
[self.navigationController popViewControllerAnimated:YES];
}
Expanding upon the answer by Abdul Baseer Khan:
For cases where the current view controller may have been loaded by different types of previous view controller, we can use the safer as? call instead of as!, which will return nil if the controller is not what we were looking for:
let previousVC = self.navigationController?.viewControllers.last as? AnExampleController
previousVC?.doSomething()
Although, you would need to repeat that for each different view controller that could load the current view controller.
So, you may want to, instead, implement a protocol to be assigned to all the possible previous view controllers:
protocol PreviousController: UIViewController {
func doSomething()
}
class AnExampleController: UIViewController, PreviousController {
// ...
func doSomething() {}
}
class AnotherController: UIViewController, PreviousController {
// ...
func doSomething() {}
}
class CurrentController: UIViewController {
// ...
func goBack() {
let previousVC = self.navigationController?.viewControllers.last as? PreviousController
previousVC?.doSomething()
}
}