Just wondering about the sender object.
I know that you can access it in the method prepareForSegue... but is it available at all in the next destinationViewController?
i.e. in the viewDidLoad could I access a segueSender object or something?
I haven't ever seen this in documentation I just thought it might be useful.
EDIT FOR CLARITY
I'm not asking how to perform a segue or find a segue or anything like this.
Say I have two view controllers (VCA and VCB).
There is a segue from VCA to VCB with the identifier "segue".
In VCA I run...
[self performSegueWithIdentifier:#"segue" sender:#"Hello world"];
My question is can I access the #"Hello world" string from VCB or is it only available in the prepareForSegue... method inside VCA?
i.e. can I access the segue sender object from the destination controller?
Yes you can.
Old question but Ill pop in an answer using swift
first you call
performSegueWithIdentifier("<#your segue identifier #>", sender: <#your object you wish to access in next viewController #>)
in prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let dest = segue.destinationViewController as! <# your destination vc #>
dest.<# sentThroughObject #> = sender
}
}
Then in vc you segue to have a var that will accept the passed through object
class NextViewController: UIViewController {
var <# sentThroughObject #> = AnyObject!()
//cast <# sentThroughObject #> as! <#your object#> to use
}
UPDATE: SWIFT 3
performSegue(withIdentifier: "<#your segue identifier #>", sender: <#Object you wish to send to next VC#>)
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
let destination = segue.destinationController as! <# cast to your destination VC #>
destination.sentViaSegueObject = sender as? <#your object#>
}
In destination VC have a property to accept the sent Object
var sentViaSegueObject = <#your object#>?
You can always get a segue from the storbyoard, and get the sourceViewController. Something like this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"myStorboard" bundle:nil];
UIStoryboardSegue *mySegue = [storyboard instantiateViewControllerWithIdentifier:#"mySegue"];
[mySegue sourceViewController];
EDIT:
Comments beat me to it. ;)
If you are trying to pass an object from one view controller to the next you may want to do something like this:
In your parent view controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Only if you have multiple segues.
if ([segue.identifier isEqualToString:#"NameOfYourSegue"]) {
// Cast necessary to access properties of the destination view controller.
[(YourChildViewController *)segue.destinationViewController setObject:theObjectToPass];
}
}
You need to give your segue a name in the storyboard and declare a property for the object to pass in the destination view controller.
Related
I am trying to do something this:
I want to pass some data to ApplicationsViewController which is embedded in nav bar, therefore I get the error: Could not cast value of type 'UINavigationController'
I am not able to understand how to implement the same. Help is much appreciated. Thank you.
The reason is you can't pass data directly to ApplicationViewController as InboxViewController segue's destination is UINavigationController.
So, first you need to access UINavigationController and after that ApplicationsViewController from the stack.
Try the code below:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToApplications" {
if let navigation = segue.destination as? UINavigationController, let applicationVC = navigation.topViewController as? ApplicationsViewController {
applicationVC.id = "RGB"
}
}
}
when pass data application view controller which is rootviewController of UINavigationController. you should give segue name of navigation controler and type cast as UINavigationController as destination viewcontroller.
Fetch rootviewController which is application View controller and assign value of id
Here is code how to assign value to Application ViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if segue.identifier == "navigationSegue" { // segue of navigationVC
let navVc = segue.destination as! UINavigationController
let appVc = navVc.viewControllers.first as! ApplicationsViewController
appvc.id = "RGB"
}
}
I have been struggling to implement an example of protocol and delegate.
My MainVC has a Modal Segue to a CategoryVC but the CategoryVC sends the user to a DetailVC.
I need to set the MainVC as the delegate for the DetailVC, but all of the examples I can find set the MainVC as the delegate of the DetailVC when the MainVC instantiates the DetailVC.
My DetailVC is not instantiated by the MainVC.
I want to accomplish
detailVC.delegate = self
But because I'm using Interface Builder Modal Segues, I don't know how to refer to the object.
Edit:
MainVC has an image being edited and the functionality to add overlays
CategoryVC has categories of overlays
DetailVC has a UICollectionView showing all of the overlays to choose from
I want DetailVC to pass to MainVC which overlay to add to the image
So the sequence goes MainVC (modal segue) -> CategoryVC (Modal segue) -> DetailVC
It sounds like CategoryVC needs to relay the info about mainVC to DetailVC.
You could give CategoryVC a property theMainVC that it would use to remember the MainVC.
So in MainVC's prepareForSegue:
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let theCategoryVC = segue.destination as? CategoryVC {
theCategoryVC.theMainVC = self
}
}
Then in your CategoryVC:
var theMainVC: MainVC?
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let theDetailVC = segue.destination as? DetailVC {
theDetailVC.delegate = theMainVC
}
}
There are many solutions to your current problem
1- Chain delegate
protocol MYDataSender {
func myDele(str:String)
}
class MainVC:UIViewController,MYDataSender{
func myDele(str:String) { print(str) }
}
when present CategoryVC
let cat = ///
cat.delegate = self // if you don't use self.present(vc ,,, then it would be inside prepareForSegue
class CategoryVC:UIViewController{
var delegate:MYDataSender!
}
when present DetailsVC
let det = //
det.delegete = delegate // rhs is CategoryVC'sdelegete = MainVC , if you don't use self.present(vc ,,, then it would be inside prepareForSegue
class DetailsVC:UIViewController{
var delegate:MYDataSender!
func send() {
delegate.myDele(str:"sendToMain")
}
}
2- NotificationCenter // the easiest
3- Shared singleton
4- Store Data
I have many annotations in a mapview (with rightCalloutAccessory buttons). The button will perform a segue from this mapview to a tableview. I want to pass the tableview a different object (that holds data) depending on which callout button was clicked.
For example: (totally made up)
annotation1 (Austin) -> pass data obj 1 (relevant to Austin)
annotation2 (Dallas) -> pass data obj 2 (relevant to Dallas)
annotation3 (Houston) -> pass data obj 3 and so on... (you get the
idea)
I am able to detect which callout button was clicked.
I'm using prepareForSegue: to pass the data obj to the destination ViewController. Since I cannot make this call take an extra argument for the data obj I require, what are some elegant ways to achieve the same effect (dynamic data obj)?
Any tip would be appreciated.
Simply grab a reference to the target view controller in prepareForSegue: method and pass any objects you need to there. Here's an example...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"YOUR_SEGUE_NAME_HERE"])
{
// Get reference to the destination view controller
YourViewController *vc = [segue destinationViewController];
// Pass any objects to the view controller here, like...
[vc setMyObjectHere:object];
}
}
REVISION: You can also use performSegueWithIdentifier:sender: method to activate the transition to a new view based on a selection or button press.
For instance, consider I had two view controllers. The first contains three buttons and the second needs to know which of those buttons has been pressed before the transition. You could wire the buttons up to an IBAction in your code which uses performSegueWithIdentifier: method, like this...
// When any of my buttons are pressed, push the next view
- (IBAction)buttonPressed:(id)sender
{
[self performSegueWithIdentifier:#"MySegue" sender:sender];
}
// This will get called too before the view appears
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"MySegue"]) {
// Get destination view
SecondView *vc = [segue destinationViewController];
// Get button tag number (or do whatever you need to do here, based on your object
NSInteger tagIndex = [(UIButton *)sender tag];
// Pass the information to your destination view
[vc setSelectedButton:tagIndex];
}
}
EDIT: The demo application I originally attached is now six years old, so I've removed it to avoid any confusion.
Sometimes it is helpful to avoid creating a compile-time dependency between two view controllers. Here's how you can do it without caring about the type of the destination view controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.destinationViewController respondsToSelector:#selector(setMyData:)]) {
[segue.destinationViewController performSelector:#selector(setMyData:)
withObject:myData];
}
}
So as long as your destination view controller declares a public property, e.g.:
#property (nonatomic, strong) MyData *myData;
you can set this property in the previous view controller as I described above.
In Swift 4.2 I would do something like that:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let yourVC = segue.destination as? YourViewController {
yourVC.yourData = self.someData
}
}
I have a sender class, like this
#class MyEntry;
#interface MySenderEntry : NSObject
#property (strong, nonatomic) MyEntry *entry;
#end
#implementation MySenderEntry
#end
I use this sender class for passing objects to prepareForSeque:sender:
-(void)didSelectItemAtIndexPath:(NSIndexPath*)indexPath
{
MySenderEntry *sender = [MySenderEntry new];
sender.entry = [_entries objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:SEGUE_IDENTIFIER_SHOW_ENTRY sender:sender];
}
-(void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_SHOW_ENTRY]) {
NSAssert([sender isKindOfClass:[MySenderEntry class]], #"MySenderEntry");
MySenderEntry *senderEntry = (MySenderEntry*)sender;
MyEntry *entry = senderEntry.entry;
NSParameterAssert(entry);
[segue destinationViewController].delegate = self;
[segue destinationViewController].entry = entry;
return;
}
if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_HISTORY]) {
// ...
return;
}
if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_FAVORITE]) {
// ...
return;
}
}
I came across this question when I was trying to learn how to pass data from one View Controller to another. I need something visual to help me learn though, so this answer is a supplement to the others already here. It is a little more general than the original question but it can be adapted to work.
This basic example works like this:
The idea is to pass a string from the text field in the First View Controller to the label in the Second View Controller.
First View Controller
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
// This function is called before the segue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// get a reference to the second view controller
let secondViewController = segue.destinationViewController as! SecondViewController
// set a variable in the second view controller with the String to pass
secondViewController.receivedString = textField.text!
}
}
Second View Controller
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
// This variable will hold the data being passed from the First View Controller
var receivedString = ""
override func viewDidLoad() {
super.viewDidLoad()
// Used the text from the First View Controller to set the label
label.text = receivedString
}
}
Remember to
Make the segue by control clicking on the button and draging it over to the Second View Controller.
Hook up the outlets for the UITextField and the UILabel.
Set the first and second View Controllers to the appropriate Swift files in IB.
Source
How to send data through segue (swift) (YouTube tutorial)
See also
View Controllers: Passing data forward and passing data back (fuller answer)
For Swift use this,
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var segueID = segue.identifier
if(segueID! == "yourSegueName"){
var yourVC:YourViewController = segue.destinationViewController as YourViewController
yourVC.objectOnYourVC = setObjectValueHere!
}
}
I've implemented a library with a category on UIViewController that simplifies this operation.
Basically, you set the parameters you want to pass over in a NSDictionary associated to the UI item that is performing the segue. It works with manual segues too.
For example, you can do
[self performSegueWithIdentifier:#"yourIdentifier" parameters:#{#"customParam1":customValue1, #"customValue2":customValue2}];
for a manual segue or create a button with a segue and use
[button setSegueParameters:#{#"customParam1":customValue1, #"customValue2":customValue2}];
If destination view controller is not key-value coding compliant for a key, nothing happens. It works with key-values too (useful for unwind segues).
Check it out here
https://github.com/stefanomondino/SMQuickSegue
My solution is similar.
// In destination class:
var AddressString:String = String()
// In segue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "seguetobiddetailpagefromleadbidder")
{
let secondViewController = segue.destinationViewController as! BidDetailPage
secondViewController.AddressString = pr.address as String
}
}
Just use this function.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let index = CategorytableView.indexPathForSelectedRow
let indexNumber = index?.row
let VC = segue.destination as! DestinationViewController
VC.value = self.data
}
I used this solution so that I could keep the invocation of the segue and the data communication within the same function:
private var segueCompletion : ((UIStoryboardSegue, Any?) -> Void)?
func performSegue(withIdentifier identifier: String, sender: Any?, completion: #escaping (UIStoryboardSegue, Any?) -> Void) {
self.segueCompletion = completion;
self.performSegue(withIdentifier: identifier, sender: sender);
self.segueCompletion = nil
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
self.segueCompletion?(segue, sender)
}
A use case would be something like:
func showData(id : Int){
someService.loadSomeData(id: id) {
data in
self.performSegue(withIdentifier: "showData", sender: self) {
storyboard, sender in
let dataView = storyboard.destination as! DataView
dataView.data = data
}
}
}
This seems to work for me, however, I'm not 100% sure that the perform and prepare functions are always executed on the same thread.
People, i want to pop a viewController using the normal back button on NavigationView controller without release the customized view that was created by the user, any one know some way to do that?
Because the natural flow of the navigation controller is release the "poped" viewController! Thanks for the help!
You need to retain a copy of the view controller elsewhere. Perhaps inside the class containing the navigation controller. Then push this back onto the stack when required.
Additionally check out UINavigationControllerDelegate
can you save the datas in the "popped" view controller? when it comes out again, populate it? When view controller is popped, it should be released.
You can navigation.viewControllers array copy in to global array before pop the custom view. After pop view global navigation.viewControllers assign global array.
NSArray create in AppDelegate
appDelegate.nav = self.navigationController.viewControllers;
[self.navigationController popViewControllerAnimated:YES];
then after assign global array in poped view
-(void)viewWillAppear:(BOOL)animated
{
self.navigationController.viewControllers = appDelegate.nav;
}
Well people tha answer is, make your controller from the StoryBoard, and don't use segue to call.
if(comparacao == nil)
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle: nil];
comparacao = [storyboard instantiateViewControllerWithIdentifier:#"ComparacaoView"];
}
[self.navigationController pushViewController:comparacao animated:YES];
So with this, I every use the before instance created, and every thing that my user do in this view was maintained.
Swift Code:
#IBAction func pushButtonAction(_ sender: UIButton) {
if let exitingSecViewCon = secondViewController {
navigationController?.pushViewController(exitingSecViewCon, animated: true)
}else{
self.performSegue(withIdentifier: "second", sender: self)
}
}
var secondViewController: SecondViewController? //Keep Strong Pointer
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "second" {
if let sec = segue.destination as? SecondViewController {
sec.name = "ABC"
secondViewController = sec;
}
}
}
Console Log:(Same address for SecondViewController)
ADD=<RetainCount.SecondViewController: 0x7fb5ac630cf0>
ADD=<RetainCount.SecondViewController: 0x7fb5ac630cf0>
ADD=<RetainCount.SecondViewController: 0x7fb5ac630cf0>
I have many annotations in a mapview (with rightCalloutAccessory buttons). The button will perform a segue from this mapview to a tableview. I want to pass the tableview a different object (that holds data) depending on which callout button was clicked.
For example: (totally made up)
annotation1 (Austin) -> pass data obj 1 (relevant to Austin)
annotation2 (Dallas) -> pass data obj 2 (relevant to Dallas)
annotation3 (Houston) -> pass data obj 3 and so on... (you get the
idea)
I am able to detect which callout button was clicked.
I'm using prepareForSegue: to pass the data obj to the destination ViewController. Since I cannot make this call take an extra argument for the data obj I require, what are some elegant ways to achieve the same effect (dynamic data obj)?
Any tip would be appreciated.
Simply grab a reference to the target view controller in prepareForSegue: method and pass any objects you need to there. Here's an example...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"YOUR_SEGUE_NAME_HERE"])
{
// Get reference to the destination view controller
YourViewController *vc = [segue destinationViewController];
// Pass any objects to the view controller here, like...
[vc setMyObjectHere:object];
}
}
REVISION: You can also use performSegueWithIdentifier:sender: method to activate the transition to a new view based on a selection or button press.
For instance, consider I had two view controllers. The first contains three buttons and the second needs to know which of those buttons has been pressed before the transition. You could wire the buttons up to an IBAction in your code which uses performSegueWithIdentifier: method, like this...
// When any of my buttons are pressed, push the next view
- (IBAction)buttonPressed:(id)sender
{
[self performSegueWithIdentifier:#"MySegue" sender:sender];
}
// This will get called too before the view appears
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"MySegue"]) {
// Get destination view
SecondView *vc = [segue destinationViewController];
// Get button tag number (or do whatever you need to do here, based on your object
NSInteger tagIndex = [(UIButton *)sender tag];
// Pass the information to your destination view
[vc setSelectedButton:tagIndex];
}
}
EDIT: The demo application I originally attached is now six years old, so I've removed it to avoid any confusion.
Sometimes it is helpful to avoid creating a compile-time dependency between two view controllers. Here's how you can do it without caring about the type of the destination view controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.destinationViewController respondsToSelector:#selector(setMyData:)]) {
[segue.destinationViewController performSelector:#selector(setMyData:)
withObject:myData];
}
}
So as long as your destination view controller declares a public property, e.g.:
#property (nonatomic, strong) MyData *myData;
you can set this property in the previous view controller as I described above.
In Swift 4.2 I would do something like that:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let yourVC = segue.destination as? YourViewController {
yourVC.yourData = self.someData
}
}
I have a sender class, like this
#class MyEntry;
#interface MySenderEntry : NSObject
#property (strong, nonatomic) MyEntry *entry;
#end
#implementation MySenderEntry
#end
I use this sender class for passing objects to prepareForSeque:sender:
-(void)didSelectItemAtIndexPath:(NSIndexPath*)indexPath
{
MySenderEntry *sender = [MySenderEntry new];
sender.entry = [_entries objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:SEGUE_IDENTIFIER_SHOW_ENTRY sender:sender];
}
-(void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_SHOW_ENTRY]) {
NSAssert([sender isKindOfClass:[MySenderEntry class]], #"MySenderEntry");
MySenderEntry *senderEntry = (MySenderEntry*)sender;
MyEntry *entry = senderEntry.entry;
NSParameterAssert(entry);
[segue destinationViewController].delegate = self;
[segue destinationViewController].entry = entry;
return;
}
if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_HISTORY]) {
// ...
return;
}
if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_FAVORITE]) {
// ...
return;
}
}
I came across this question when I was trying to learn how to pass data from one View Controller to another. I need something visual to help me learn though, so this answer is a supplement to the others already here. It is a little more general than the original question but it can be adapted to work.
This basic example works like this:
The idea is to pass a string from the text field in the First View Controller to the label in the Second View Controller.
First View Controller
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
// This function is called before the segue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// get a reference to the second view controller
let secondViewController = segue.destinationViewController as! SecondViewController
// set a variable in the second view controller with the String to pass
secondViewController.receivedString = textField.text!
}
}
Second View Controller
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
// This variable will hold the data being passed from the First View Controller
var receivedString = ""
override func viewDidLoad() {
super.viewDidLoad()
// Used the text from the First View Controller to set the label
label.text = receivedString
}
}
Remember to
Make the segue by control clicking on the button and draging it over to the Second View Controller.
Hook up the outlets for the UITextField and the UILabel.
Set the first and second View Controllers to the appropriate Swift files in IB.
Source
How to send data through segue (swift) (YouTube tutorial)
See also
View Controllers: Passing data forward and passing data back (fuller answer)
For Swift use this,
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var segueID = segue.identifier
if(segueID! == "yourSegueName"){
var yourVC:YourViewController = segue.destinationViewController as YourViewController
yourVC.objectOnYourVC = setObjectValueHere!
}
}
I've implemented a library with a category on UIViewController that simplifies this operation.
Basically, you set the parameters you want to pass over in a NSDictionary associated to the UI item that is performing the segue. It works with manual segues too.
For example, you can do
[self performSegueWithIdentifier:#"yourIdentifier" parameters:#{#"customParam1":customValue1, #"customValue2":customValue2}];
for a manual segue or create a button with a segue and use
[button setSegueParameters:#{#"customParam1":customValue1, #"customValue2":customValue2}];
If destination view controller is not key-value coding compliant for a key, nothing happens. It works with key-values too (useful for unwind segues).
Check it out here
https://github.com/stefanomondino/SMQuickSegue
My solution is similar.
// In destination class:
var AddressString:String = String()
// In segue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "seguetobiddetailpagefromleadbidder")
{
let secondViewController = segue.destinationViewController as! BidDetailPage
secondViewController.AddressString = pr.address as String
}
}
Just use this function.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let index = CategorytableView.indexPathForSelectedRow
let indexNumber = index?.row
let VC = segue.destination as! DestinationViewController
VC.value = self.data
}
I used this solution so that I could keep the invocation of the segue and the data communication within the same function:
private var segueCompletion : ((UIStoryboardSegue, Any?) -> Void)?
func performSegue(withIdentifier identifier: String, sender: Any?, completion: #escaping (UIStoryboardSegue, Any?) -> Void) {
self.segueCompletion = completion;
self.performSegue(withIdentifier: identifier, sender: sender);
self.segueCompletion = nil
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
self.segueCompletion?(segue, sender)
}
A use case would be something like:
func showData(id : Int){
someService.loadSomeData(id: id) {
data in
self.performSegue(withIdentifier: "showData", sender: self) {
storyboard, sender in
let dataView = storyboard.destination as! DataView
dataView.data = data
}
}
}
This seems to work for me, however, I'm not 100% sure that the perform and prepare functions are always executed on the same thread.