Passing information between UICollectionViewControllers through unwind segues - ios

I have two UICollectionViewControllers and the first one uses a push segue to get to the second one. The problem I'm having is passing information back to the first controller when the back button (the one that gets added automagically) is pressed in the second controller. I've tried using the segueForUnwindingToViewController, and canPerformUnwindSegueAction override functions, but no dice. I need to be able to access both view controllers so I can set some variables. Any ideas?

Here is an example with two view controllers. Let's say that the names of the two view controllers and ViewController and SecondViewController. Let's also say that there is an unwind segue from the SecondViewController to the ViewController. We will pass data from the SecondViewController to the ViewController. First, let's set the identifier of this segue by opening the document outline and selecting the unwind segue. Then open up the attributes inspector and set the identifier to "unwind".
SecondViewController Code:
class SecondViewController: UIViewController
{
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
if let destination = segue.destinationViewController as? ViewController {
if identifier == "unwind" {
destination.string = "We Just Passed Data"
}
}
}
}
}
ViewController Code:
class ViewController: UIViewController {
var string = "The String That Will Be We Just Passed Data"
#IBAction func unwindSegue(segue: UIStoryBoardSegue) {
}
}

It sounds like you are trying to intercept the back button, there are many posts for this on SO, here are two:
Setting action for back button in navigation controller
Trying to handle "back" navigation button action in iOS
In practice, it is more clear to return state in closures (more modern), or delegates.

Related

Find out from where ViewController was opened

I have a TableViewController and 2 ways to get there.
one is a segue(show) from a Viewcontroller that is the root controller of a Navigation Controller, which itself is a tab of my Tab Bar Controller.
second, the tableVC is also a root VC of antoher Navigation Controller, that is also a tab in that Tab Bar Controller. Here is an illustration:
Now i want to check in the viewDidLoad if my TableVC, whether it is called by the first or by the second way. How can i find that out?
You could add a property to your view controller that indicates where it came from…
class MyTableViewController: UITableViewController {
enum Source {
case productList, basket
}
var source: Source!
}
then
override func prepareForSegue(segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? MyTableViewController {
vc.source = .productList
}
}
etc
very simple just add a var on your viewController which you are going to display , lets assume var vcOpenedBy = ""
now when launching this ViewController , just use this var and print whatever you feel comfortable like this
let vc = VcController(nibName:"",bundle:nil)
vc.vcOpenedBy = "NavigationMethod"
and at your segue you can also use this vcOpenedBy
and use string "SqgueMethod"
now on that viewController in viewDidLoad()
just print this vc . thats it

How to identify the segue identifier on destination view controller?

I have two view controllers, the first one has two buttons, signup and login, the second VC does the function of signup and login stuff (I wrote functions to switch between signup and login mode), is it possible to identify if user pressed login/signup button in the first VC so the right function will be called in the second VC when performing segue?
You have tell the second view controller what to do upon the first view controller selected option (signin or signup). I would assume that you could do this by simply declaring a flag and send it to the second view controller, for instance:
Declare a boolean variable in your second view controller (let's say shouldBehavesAsLogin) which means if selection is login it should be true:
// Controller that could represents signin or signup:
class SecondViewController: UIViewController {
//...
var shouldBehavesAsLogin = false
// ...
}
thus you could determine what is the value that should be assigned to it based on which button tapped, first view controller:
class FirstViewController: UIViewController {
// ...
private var isLoginTapped = false
#IBAction func signinTapped(sender: UIButton) {
isLoginTapped = true
}
#IBAction func signupTapped(sender: UIButton) {
// nothing to do here, isLoginTapped is false by default...
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MySegue"{
if let nextViewController = segue.destination as? SecondViewController {
nextViewController.shouldBehavesAsLogin = isLoginTapped
}
}
}
// ...
}
Thus all you have to do is to check the value of shouldBehavesAsLogin whether is it true to let the controller behaves as login or false to do the opposite.
Additional Tip:
If the purpose of adding IBActions for each button is just navigating to the second view controller, I would suggest to let both of the buttons to have the same IBAction, but you should let the sender to be of type UIButton instead of Any, thus you could do -for instance-:
#IBAction func aButtonTapped(sender: UIButton) {
// do the default behvior for both signin and signup (navigate to the second controller)
// signinButton is the button you tap for navigating to second controller to behaves as signin
isLoginTapped = sender === signinButton ? true : false
}
This answer assumes there is only one segue between the two view controllers. If there is more than one, you can simply use the segue identifier in prepareForSegue.
I'd handle this by using the sender parameter on either -prepare(for segue:sender:) or -performSegue(withIdentifier:sender), depending on whether you are triggering the segue directly from the button or in code.
If the buttons are triggering the segue, you can test whether the sender params is one of the two buttons concerned, and perform appropriate setup on the destination VC. If the segue is being triggered in code in an IBAction method, you could either pass through the button reference from the IBActions sender parameter, or pass some other identifying object that your prepareForSegue method is able to deal with.
Bear in mind that the target VC's views will not have loaded when prepareForSegue is called, so you may need to set some state on the target VC (e.g. a login/signup enum property) which is then used to set up the VC appropriately when its -viewDidLoad is called.

How to pop back to a TableViewController?

I start of with a tableViewController that has a list of names. When the user taps on a name, they are segued to a view controller.
While in that viewController the user may press a button that will take them to another table view Controller.
The layout is like this:
TableViewController(1) -> ViewController -> TableViewController(2)
My question is, how can I pop back to the first TableViewController from the Second TableViewController.
My rootViewController is my signIn View controller so I cannot pop back to root.
You can run this to pop to your rootViewController:
self.navigationController?.popToRootViewController(animated: true)
Update:
Since your rootViewController is not where you want to end up then you can iterate through your controllers and pop to a specific one:
for controller in self.navigationController!.viewControllers {
if controller.isKind(of: TableViewControllerOne.self) {
self.navigationController!.popToViewController(controller, animated: true)
break
}
}
Instead of TableViewControllerOne.self update to your desired controller.
If you're familiar with segues, you can implement an unwind segue. That would give you the added benefit of passing information back to TableViewController(1) if you needed to. To make that work in TableViewController1 you would add some code that looked like:
#IBAction func unwind(fromTableVC2 segue: UIStoryboardSegue) {
if (segue.source is TableVC2) {
if let svc = segue.source as? TableVC2 {
// pass information back
}
}
}
Then in your storyboard you would go to where you have your TableVC2 and drag the yellow VC circle to the exit and choose the function we created above. Name the segue (for this example we'll call it "UnwindToTableVC1"), and then somewhere in TableVC2 add the code:
func setVariableToPassBack () {
// Set up variables you want to pass back
performSegue(withIdentifier: "UnwindToTableVC1", sender: self) }
And that will take you back to your chosen destination with any information you wanted to pass back.
If you don't want to pass anything back, you really just need call the below line in your TableVC2:
performSegue(withIdentifier: "UnwindToTableVC1", sender: self)

iOS Button Trigger Segue & Send Data

I have a registration view controller. In it I process the input provided by the user soon as he clicks on the register button. This button in the view is connected to an IBAction where I validate the input provided.
How is t possible to trigger the segue and pass the data within the IBAction (not override prepareforsegue) to the next view controller? If the validation fails it shouldn't attempt to execute segue but rather stay on same view to display validation errors.
Thanks for support. The question has been asked in different ways before surely but I wasn't able to find a good combine of segue in ibaction and passing data too.
You can create a segue from one view controller to another and can put a check in your button's IBAction, like this:
if (validationPasses)
{
self.performSegueWithIdentifier("segueIdentifier", sender: self)
}
And if you don't won't to override your prepareforsegue, you can pass the data using delegates.
However, by overriding prepareforsegue, you can pass data to your next view controller like this:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "segueIdentifier") {
// pass data to next view controller
let vc : NextViewController = segue!.destinationViewController as NextViewController
vc.someVariable = someValue
}
}
Here NextViewController is your next viewcontroller, change its name to your next view controller and in this view controller define variable to whom you want to pass value, in this case it is someVariable

Modifing one variable from another view controller swift

I am developing an app in Swift that, in gist, tells people the price of Bitcoin in various currencies. To select the currency, the user chooses from a list in a view controller with a UITableView. This is currencyViewController, and it is presented from my main screen, viewController.
What I want to happen is that, when the user dismisses currencyViewController, it passes a string to a UIButton in the main viewController.
Here's the prepareForSegue function that should pass the data:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "presentCurrency") {
currencySelector.setTitle("\currencySelected", forState: UIControlState.Normal)
}
}
CurrencySelector is a UIButton in the main viewController, and currencySelected is a variable in the second view controller, currencyViewController.
It gives the error "Invalid Escape Sequence In Literal"
So, I've narrowed it down to one of two issues:
The variables from viewController can't be "seen" from currencyViewController. If so, how can I modify the text of CurrencySelector from CurrencyViewController?
For some reason, when the user exits the pushed CurrencyViewControler, prepareForSegue isn't called.
What is going on here? Thanks, and apologies - I am but a swift newbie.
2 - "prepareForSegue" is called when you push a new view controller via the segue, but not when you dismiss it. No segue is called upon dismissal.
1 - A good way to do this would be the delegate pattern.
So the main view controller would be the delegate for the currencyViewController, and would receive a message when that controller is dismissed.
In the start of the currencyViewController file you prepare the delegate:
protocol CurrencyViewControllerDelegate {
func currencyViewControllerDidSelect(value: String)
}
and you add a variable to the currencyViewController:
var delegate : CurrencyViewControllerDelegate?
Now, the mainViewController has to conform to that protocol and answer to that function:
class MainViewController : UIViewController, CurrencyViewControllerDelegate {
//...
func currencyViewControllerDidSelect(value: String) {
//do your stuff here
}
}
And everything is prepared. Last steps, in prepareForSegue (MainViewController), you will set the delegate of the currencyViewController:
var currencyVC = segue.destinationViewController as CurrencyViewController
currencyVC.delegate = self;
And when the user selects the value in currencyViewController, just call that function in the delegate:
self.delegate?.currencyViewControllerDidSelect("stuff")
A bit complex maybe, but it's a very useful pattern :) here is a nice tutorial with more info if you want it:
http://www.raywenderlich.com/75289/swift-tutorial-part-3-tuples-protocols-delegates-table-views
You have to use the parantheses to eval variables in strings, i.e. println("\(currencySelected)")
To access variables in the second view controller (the one which is the destination of the segue) you have to get a reference to it:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "presentCurrency") {
let currencyViewController = segue.destinationViewController as CurrencyViewController // the name or your class here
currencySelector.setTitle("\(currencyViewController.currencySelected)", forState: UIControlState.Normal)
}
}

Resources